From ae36d4f72ad8b34343dba391f2f239ff29840117 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 25 Jun 2015 15:30:00 -0700 Subject: [PATCH 1/4] mk: Add support for i686-pc-windows-msvc This commit modifies the configure script and our makefiles to support building 32-bit MSVC targets. The MSVC toolchain is now parameterized over whether it can produce a 32-bit or 64-bit binary. The configure script was updated to export more variables at configure time, and the makefiles were rejiggered to selectively reexport the relevant environment variables for the applicable targets they're going to run for. --- configure | 60 +++++++++++++++++++++++++------- mk/cfg/i686-pc-windows-msvc.mk | 29 +++++++++++++++ mk/cfg/x86_64-pc-windows-msvc.mk | 10 +++--- mk/platform.mk | 18 +--------- mk/rt.mk | 19 ++++++++++ mk/target.mk | 37 ++++++++++++++++++++ 6 files changed, 138 insertions(+), 35 deletions(-) create mode 100644 mk/cfg/i686-pc-windows-msvc.mk diff --git a/configure b/configure index 891f524a706e0..5d4a017b6fbfa 100755 --- a/configure +++ b/configure @@ -1114,7 +1114,7 @@ do fi ;; - x86_64-*-msvc) + *-msvc) # Currently the build system is not configured to build jemalloc # with MSVC, so we omit this optional dependency. step_msg "targeting MSVC, disabling jemalloc" @@ -1154,22 +1154,45 @@ do CFG_MSVC_ROOT=$(echo "$install" | grep InstallDir | sed 's/.*REG_SZ[ ]*//') CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT") CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT") - CFG_MSVC_CL="${CFG_MSVC_ROOT}/VC/bin/amd64/cl.exe" - CFG_MSVC_LIB="${CFG_MSVC_ROOT}/VC/bin/amd64/lib.exe" - CFG_MSVC_LINK="${CFG_MSVC_ROOT}/VC/bin/amd64/link.exe" + putvar CFG_MSVC_ROOT + + case $i in + x86_64-*) + bits=x86_64 + msvc_part=amd64 + ;; + i686-*) + bits=i386 + msvc_part= + ;; + *) + err "can only target x86 targets for MSVC" + ;; + esac + bindir="${CFG_MSVC_ROOT}/VC/bin" + if [ ! -z "$msvc_part" ]; then + bindir="$bindir/$msvc_part" + fi + eval CFG_MSVC_BINDIR_$bits="\"$bindir\"" + eval CFG_MSVC_CL_$bits="\"$bindir/cl.exe\"" + eval CFG_MSVC_LIB_$bits="\"$bindir/lib.exe\"" + eval CFG_MSVC_LINK_$bits="\"$bindir/link.exe\"" vcvarsall="${CFG_MSVC_ROOT}/VC/vcvarsall.bat" - CFG_MSVC_INCLUDE_PATH=$(cmd /c "\"$vcvarsall\" amd64 && cmd /c echo %INCLUDE%") + include_path=$(cmd /c "\"$vcvarsall\" $msvc_part && cmd /c echo %INCLUDE%") need_ok "failed to learn about MSVC's INCLUDE" - CFG_MSVC_LIB_PATH=$(cmd /c "\"$vcvarsall\" amd64 && cmd /c echo %LIB%") + lib_path=$(cmd /c "\"$vcvarsall\" $msvc_part && cmd /c echo %LIB%") need_ok "failed to learn about MSVC's LIB" - putvar CFG_MSVC_ROOT - putvar CFG_MSVC_CL - putvar CFG_MSVC_LIB - putvar CFG_MSVC_LINK - putvar CFG_MSVC_INCLUDE_PATH - putvar CFG_MSVC_LIB_PATH + eval CFG_MSVC_INCLUDE_PATH_${bits}="\"$include_path\"" + eval CFG_MSVC_LIB_PATH_${bits}="\"$lib_path\"" + + putvar CFG_MSVC_BINDIR_${bits} + putvar CFG_MSVC_CL_${bits} + putvar CFG_MSVC_LIB_${bits} + putvar CFG_MSVC_LINK_${bits} + putvar CFG_MSVC_INCLUDE_PATH_${bits} + putvar CFG_MSVC_LIB_PATH_${bits} ;; *) @@ -1408,8 +1431,19 @@ do msg "configuring LLVM with:" msg "$CMAKE_ARGS" + case "$t" in + x86_64-*) + generator="Visual Studio 12 2013 Win64" + ;; + i686-*) + generator="Visual Studio 12 2013" + ;; + *) + err "can only build LLVM for x86 platforms" + ;; + esac (cd $LLVM_BUILD_DIR && "$CFG_CMAKE" $CFG_LLVM_SRC_DIR \ - -G "Visual Studio 12 2013 Win64" \ + -G "$generator" \ $CMAKE_ARGS) need_ok "LLVM cmake configure failed" fi diff --git a/mk/cfg/i686-pc-windows-msvc.mk b/mk/cfg/i686-pc-windows-msvc.mk new file mode 100644 index 0000000000000..bb1280688a716 --- /dev/null +++ b/mk/cfg/i686-pc-windows-msvc.mk @@ -0,0 +1,29 @@ +# i686-pc-windows-msvc configuration +CC_i686-pc-windows-msvc="$(CFG_MSVC_CL_i386)" -nologo +LINK_i686-pc-windows-msvc="$(CFG_MSVC_LINK_i386)" -nologo +CXX_i686-pc-windows-msvc="$(CFG_MSVC_CL_i386)" -nologo +CPP_i686-pc-windows-msvc="$(CFG_MSVC_CL_i386)" -nologo +AR_i686-pc-windows-msvc="$(CFG_MSVC_LIB_i386)" -nologo +CFG_LIB_NAME_i686-pc-windows-msvc=$(1).dll +CFG_STATIC_LIB_NAME_i686-pc-windows-msvc=$(1).lib +CFG_LIB_GLOB_i686-pc-windows-msvc=$(1)-*.{dll,lib} +CFG_LIB_DSYM_GLOB_i686-pc-windows-msvc=$(1)-*.dylib.dSYM +CFG_JEMALLOC_CFLAGS_i686-pc-windows-msvc := +CFG_GCCISH_CFLAGS_i686-pc-windows-msvc := -MD +CFG_GCCISH_CXXFLAGS_i686-pc-windows-msvc := -MD +CFG_GCCISH_LINK_FLAGS_i686-pc-windows-msvc := +CFG_GCCISH_DEF_FLAG_i686-pc-windows-msvc := +CFG_LLC_FLAGS_i686-pc-windows-msvc := +CFG_INSTALL_NAME_i686-pc-windows-msvc = +CFG_EXE_SUFFIX_i686-pc-windows-msvc := .exe +CFG_WINDOWSY_i686-pc-windows-msvc := 1 +CFG_UNIXY_i686-pc-windows-msvc := +CFG_LDPATH_i686-pc-windows-msvc := +CFG_RUN_i686-pc-windows-msvc=$(2) +CFG_RUN_TARG_i686-pc-windows-msvc=$(call CFG_RUN_i686-pc-windows-msvc,,$(2)) +CFG_GNU_TRIPLE_i686-pc-windows-msvc := i686-pc-win32 + +# All windows nightiles are currently a GNU triple, so this MSVC triple is not +# bootstrapping from itself. This is relevant during stage0, and other parts of +# the build system take this into account. +BOOTSTRAP_FROM_i686-pc-windows-msvc := i686-pc-windows-gnu diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk index edeffcdd09b9b..6f12836f05624 100644 --- a/mk/cfg/x86_64-pc-windows-msvc.mk +++ b/mk/cfg/x86_64-pc-windows-msvc.mk @@ -1,9 +1,9 @@ # x86_64-pc-windows-msvc configuration -CC_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo -LINK_x86_64-pc-windows-msvc="$(CFG_MSVC_LINK)" -nologo -CXX_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo -CPP_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo -AR_x86_64-pc-windows-msvc="$(CFG_MSVC_LIB)" -nologo +CC_x86_64-pc-windows-msvc="$(CFG_MSVC_CL_x86_64)" -nologo +LINK_x86_64-pc-windows-msvc="$(CFG_MSVC_LINK_x86_64)" -nologo +CXX_x86_64-pc-windows-msvc="$(CFG_MSVC_CL_x86_64)" -nologo +CPP_x86_64-pc-windows-msvc="$(CFG_MSVC_CL_x86_64)" -nologo +AR_x86_64-pc-windows-msvc="$(CFG_MSVC_LIB_x86_64)" -nologo CFG_LIB_NAME_x86_64-pc-windows-msvc=$(1).dll CFG_STATIC_LIB_NAME_x86_64-pc-windows-msvc=$(1).lib CFG_LIB_GLOB_x86_64-pc-windows-msvc=$(1)-*.{dll,lib} diff --git a/mk/platform.mk b/mk/platform.mk index abc9cc038d022..60fe22cb32ee6 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -239,23 +239,6 @@ endef $(foreach target,$(CFG_TARGET), \ $(eval $(call CFG_MAKE_TOOLCHAIN,$(target)))) -# These two environment variables are scraped by the `./configure` script and -# are necessary for `cl.exe` to find standard headers (the INCLUDE variable) and -# for `link.exe` to find standard libraries (the LIB variable). -ifdef CFG_MSVC_INCLUDE_PATH -export INCLUDE := $(CFG_MSVC_INCLUDE_PATH) -endif -ifdef CFG_MSVC_LIB_PATH -export LIB := $(CFG_MSVC_LIB_PATH) -endif - -# Unfortunately `link.exe` is also a program in `/usr/bin` on MinGW installs, -# but it's not the one that we want. As a result we make sure that our detected -# `link.exe` shows up in PATH first. -ifdef CFG_MSVC_LINK -export PATH := $(CFG_MSVC_ROOT)/VC/bin/amd64:$(PATH) -endif - # There are more comments about this available in the target specification for # Windows MSVC in the compiler, but the gist of it is that we use `llvm-ar.exe` # instead of `lib.exe` for assembling archives, so we need to inject this custom @@ -307,3 +290,4 @@ endef $(foreach target,$(CFG_TARGET), \ $(eval $(call ADD_RUSTC_LLVM_DEF_TO_MSVC,$(target)))) + diff --git a/mk/rt.mk b/mk/rt.mk index 6513cf107726a..c70f9e8a37add 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -55,7 +55,11 @@ NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \ rust_android_dummy.c NATIVE_DEPS_rustrt_native_$(1) := arch/$$(HOST_$(1))/record_sp.S ifeq ($$(findstring msvc,$(1)),msvc) +ifeq ($$(findstring i686,$(1)),i686) +NATIVE_DEPS_rustrt_native_$(1) += rust_try_msvc_32.ll +else NATIVE_DEPS_rustrt_native_$(1) += rust_try_msvc_64.ll +endif else NATIVE_DEPS_rustrt_native_$(1) += rust_try.ll endif @@ -93,6 +97,17 @@ $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.S $$(MKFILE_DEPS) \ @mkdir -p $$(@D) @$$(call E, compile: $$@) $$(Q)$$(call CFG_ASSEMBLE_$(1),$$@,$$<) + +# On MSVC targets the compiler's default include path (e.g. where to find system +# headers) is specified by the INCLUDE environment variable. This may not be set +# so the ./configure script scraped the relevant values and this is the location +# that we put them into cl.exe's environment. +ifeq ($$(findstring msvc,$(1)),msvc) +$$(RT_OUTPUT_DIR_$(1))/%.o: \ + export INCLUDE := $$(CFG_MSVC_INCLUDE_PATH_$$(HOST_$(1))) +$(1)/rustllvm/%.o: \ + export INCLUDE := $$(CFG_MSVC_INCLUDE_PATH_$$(HOST_$(1))) +endif endef $(foreach target,$(CFG_TARGET),$(eval $(call NATIVE_LIBRARIES,$(target)))) @@ -240,8 +255,12 @@ COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) ifeq ($$(findstring msvc,$(1)),msvc) COMPRT_CC_$(1) := gcc COMPRT_AR_$(1) := ar +ifeq ($$(findstring i686,$(1)),i686) +COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m32 +else COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m64 endif +endif $$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) @$$(call E, make: compiler-rt) diff --git a/mk/target.mk b/mk/target.mk index 3c274dc4fd5f2..c398950965f54 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -220,3 +220,40 @@ $(foreach target,$(CFG_TARGET), \ $(foreach crate,$(CRATES), \ $(foreach tool,$(NATIVE_TOOL_DEPS_$(crate)_T_$(target)), \ $(eval $(call MOVE_TOOLS_TO_SNAPSHOT_HOST_DIR,0,$(target),$(BOOTSTRAP_FROM_$(target)),$(crate),$(tool)))))) + +# For MSVC targets we need to set up some environment variables for the linker +# to work correctly when building Rust crates. These two variables are: +# +# - LIB tells the linker the default search path for finding system libraries, +# for example kernel32.dll +# - PATH needs to be modified to ensure that MSVC's link.exe is first in the +# path instead of MinGW's /usr/bin/link.exe (entirely unrelated) +# +# The values for these variables are detected by the configure script. +define SETUP_LIB_MSVC_ENV_VARS +ifeq ($$(findstring msvc,$(2)),msvc) +$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \ + export LIB := $$(CFG_MSVC_LIB_PATH_$$(HOST_$(2))) +$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \ + export PATH := $$(CFG_MSVC_BINDIR_$$(HOST_$(2))):$$(PATH) +endif +endef +define SETUP_TOOL_MSVC_ENV_VARS +ifeq ($$(findstring msvc,$(2)),msvc) +$$(TBIN$(1)_T_$(2)_H_$(3))/$(4)$$(X_$(2)): \ + export LIB := $$(CFG_MSVC_LIB_PATH_$$(HOST_$(2))) +$$(TBIN$(1)_T_$(2)_H_$(3))/$(4)$$(X_$(2)): \ + export PATH := $$(CFG_MSVC_BINDIR_$$(HOST_$(2))):$$(PATH) +endif +endef + +$(foreach host,$(CFG_HOST), \ + $(foreach target,$(CFG_TARGET), \ + $(foreach stage,$(STAGES), \ + $(foreach crate,$(CRATES), \ + $(eval $(call SETUP_LIB_MSVC_ENV_VARS,$(stage),$(target),$(host),$(crate))))))) +$(foreach host,$(CFG_HOST), \ + $(foreach target,$(CFG_TARGET), \ + $(foreach stage,$(STAGES), \ + $(foreach tool,$(TOOLS), \ + $(eval $(call SETUP_TOOL_MSVC_ENV_VARS,$(stage),$(target),$(host),$(tool))))))) From 5de665e8b31fc0459839c6377157b9559e4679cc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 25 Jun 2015 15:32:10 -0700 Subject: [PATCH 2/4] rustc_back: Learn about i686-pc-windows-msvc This commit adds the i686-pc-windows-msvc triple to the compiler's repertoire of triples to prepare for targeting 32-bit MSVC. --- .../target/i686_pc_windows_msvc.rs | 27 +++++++++++++++++++ src/librustc_back/target/mod.rs | 3 ++- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/librustc_back/target/i686_pc_windows_msvc.rs diff --git a/src/librustc_back/target/i686_pc_windows_msvc.rs b/src/librustc_back/target/i686_pc_windows_msvc.rs new file mode 100644 index 0000000000000..d71aa1526660e --- /dev/null +++ b/src/librustc_back/target/i686_pc_windows_msvc.rs @@ -0,0 +1,27 @@ +// 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 target::Target; + +pub fn target() -> Target { + let mut base = super::windows_msvc_base::opts(); + base.cpu = "i686".to_string(); + + Target { + data_layout: "e-p:32:32-f64:64:64-i64:64:64-f80:32:32-n8:16:32".to_string(), + llvm_target: "i686-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + arch: "x86".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + options: base, + } +} diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 402fbcd8d8d8e..a42f861d19056 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -381,7 +381,8 @@ impl Target { x86_64_pc_windows_gnu, i686_pc_windows_gnu, - x86_64_pc_windows_msvc + x86_64_pc_windows_msvc, + i686_pc_windows_msvc ); From 87909582374be91bc5affdd2e74e265077a6e571 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 25 Jun 2015 15:33:42 -0700 Subject: [PATCH 3/4] std: Avoid missing fns on i686-pc-windows-msvc It turns out that the 32-bit toolchain for MSVC has many of these functions as `static inline` functions in header files so there's not actually a symbol for Rust to call. All of the implementations just cast floats to their 64-bit variants and then cast back to 32-bit at the end, so the standard library now takes this strategy. --- src/libcore/num/f32.rs | 60 +++++++++++++++++++++++--- src/libcore/ops.rs | 44 ++++++++++++------- src/libstd/num/f32.rs | 95 ++++++++++++++++++++++++++++++++---------- 3 files changed, 157 insertions(+), 42 deletions(-) diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index aade9061657b7..c8f95a3672d4a 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -215,13 +215,37 @@ impl Float for f32 { /// Rounds towards minus infinity. #[inline] fn floor(self) -> f32 { - unsafe { intrinsics::floorf32(self) } + return floorf(self); + + // On MSVC LLVM will lower many math intrinsics to a call to the + // corresponding function. On MSVC, however, many of these functions + // aren't actually available as symbols to call, but rather they are all + // `static inline` functions in header files. This means that from a C + // perspective it's "compatible", but not so much from an ABI + // perspective (which we're worried about). + // + // The inline header functions always just cast to a f64 and do their + // operation, so we do that here as well, but only for MSVC targets. + // + // Note that there are many MSVC-specific float operations which + // redirect to this comment, so `floorf` is just one case of a missing + // function on MSVC, but there are many others elsewhere. + #[cfg(target_env = "msvc")] + fn floorf(f: f32) -> f32 { (f as f64).floor() as f32 } + #[cfg(not(target_env = "msvc"))] + fn floorf(f: f32) -> f32 { unsafe { intrinsics::floorf32(f) } } } /// Rounds towards plus infinity. #[inline] fn ceil(self) -> f32 { - unsafe { intrinsics::ceilf32(self) } + return ceilf(self); + + // see notes above in `floor` + #[cfg(target_env = "msvc")] + fn ceilf(f: f32) -> f32 { (f as f64).ceil() as f32 } + #[cfg(not(target_env = "msvc"))] + fn ceilf(f: f32) -> f32 { unsafe { intrinsics::ceilf32(f) } } } /// Rounds to nearest integer. Rounds half-way cases away from zero. @@ -299,7 +323,13 @@ impl Float for f32 { #[inline] fn powf(self, n: f32) -> f32 { - unsafe { intrinsics::powf32(self, n) } + return powf(self, n); + + // see notes above in `floor` + #[cfg(target_env = "msvc")] + fn powf(f: f32, n: f32) -> f32 { (f as f64).powf(n as f64) as f32 } + #[cfg(not(target_env = "msvc"))] + fn powf(f: f32, n: f32) -> f32 { unsafe { intrinsics::powf32(f, n) } } } #[inline] @@ -317,7 +347,13 @@ impl Float for f32 { /// Returns the exponential of the number. #[inline] fn exp(self) -> f32 { - unsafe { intrinsics::expf32(self) } + return expf(self); + + // see notes above in `floor` + #[cfg(target_env = "msvc")] + fn expf(f: f32) -> f32 { (f as f64).exp() as f32 } + #[cfg(not(target_env = "msvc"))] + fn expf(f: f32) -> f32 { unsafe { intrinsics::expf32(f) } } } /// Returns 2 raised to the power of the number. @@ -329,7 +365,13 @@ impl Float for f32 { /// Returns the natural logarithm of the number. #[inline] fn ln(self) -> f32 { - unsafe { intrinsics::logf32(self) } + return logf(self); + + // see notes above in `floor` + #[cfg(target_env = "msvc")] + fn logf(f: f32) -> f32 { (f as f64).ln() as f32 } + #[cfg(not(target_env = "msvc"))] + fn logf(f: f32) -> f32 { unsafe { intrinsics::logf32(f) } } } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -345,7 +387,13 @@ impl Float for f32 { /// Returns the base 10 logarithm of the number. #[inline] fn log10(self) -> f32 { - unsafe { intrinsics::log10f32(self) } + return log10f(self); + + // see notes above in `floor` + #[cfg(target_env = "msvc")] + fn log10f(f: f32) -> f32 { (f as f64).log10() as f32 } + #[cfg(not(target_env = "msvc"))] + fn log10f(f: f32) -> f32 { unsafe { intrinsics::log10f32(f) } } } /// Converts to degrees, assuming the number is in radians. diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 48b1cbeef4fdd..9a22fe3a493f1 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -419,26 +419,40 @@ macro_rules! rem_impl { )*) } -macro_rules! rem_float_impl { - ($t:ty, $fmod:ident) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl Rem for $t { - type Output = $t; +rem_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 } - #[inline] - fn rem(self, other: $t) -> $t { - extern { fn $fmod(a: $t, b: $t) -> $t; } - unsafe { $fmod(self, other) } - } - } +#[stable(feature = "rust1", since = "1.0.0")] +impl Rem for f32 { + type Output = f32; + + // see notes in `core::f32::Float::floor` + #[inline] + #[cfg(target_env = "msvc")] + fn rem(self, other: f32) -> f32 { + (self as f64).rem(other as f64) as f32 + } - forward_ref_binop! { impl Rem, rem for $t, $t } + #[inline] + #[cfg(not(target_env = "msvc"))] + fn rem(self, other: f32) -> f32 { + extern { fn fmodf(a: f32, b: f32) -> f32; } + unsafe { fmodf(self, other) } } } -rem_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 } -rem_float_impl! { f32, fmodf } -rem_float_impl! { f64, fmod } +#[stable(feature = "rust1", since = "1.0.0")] +impl Rem for f64 { + type Output = f64; + + #[inline] + fn rem(self, other: f64) -> f64 { + extern { fn fmod(a: f64, b: f64) -> f64; } + unsafe { fmod(self, other) } + } +} + +forward_ref_binop! { impl Rem, rem for f64, f64 } +forward_ref_binop! { impl Rem, rem for f32, f32 } /// The `Neg` trait is used to specify the functionality of unary `-`. /// diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 0c40f6c1fc8a8..c2fb2fa417598 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -18,6 +18,7 @@ use prelude::v1::*; use core::num; +#[cfg(not(target_env = "msvc"))] use intrinsics; use libc::c_int; use num::{FpCategory, ParseFloatError}; @@ -33,12 +34,7 @@ mod cmath { use libc::{c_float, c_int}; extern { - pub fn acosf(n: c_float) -> c_float; - pub fn asinf(n: c_float) -> c_float; - pub fn atanf(n: c_float) -> c_float; - pub fn atan2f(a: c_float, b: c_float) -> c_float; pub fn cbrtf(n: c_float) -> c_float; - pub fn coshf(n: c_float) -> c_float; pub fn erff(n: c_float) -> c_float; pub fn erfcf(n: c_float) -> c_float; pub fn expm1f(n: c_float) -> c_float; @@ -51,32 +47,77 @@ mod cmath { pub fn log1pf(n: c_float) -> c_float; pub fn ilogbf(n: c_float) -> c_int; pub fn modff(n: c_float, iptr: &mut c_float) -> c_float; - pub fn sinhf(n: c_float) -> c_float; - pub fn tanf(n: c_float) -> c_float; - pub fn tanhf(n: c_float) -> c_float; pub fn tgammaf(n: c_float) -> c_float; #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgammaf_r")] pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypotf")] pub fn hypotf(x: c_float, y: c_float) -> c_float; + } - #[cfg(any(unix, all(windows, not(target_env = "msvc"))))] + // See the comments in `core::float::Float::floor` for why MSVC is special + // here. + #[cfg(not(target_env = "msvc"))] + extern { + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; - #[cfg(any(unix, all(windows, not(target_env = "msvc"))))] pub fn ldexpf(x: c_float, n: c_int) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; } - #[cfg(all(windows, target_env = "msvc"))] - pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { - f64::ldexp(x as f64, n as isize) as c_float - } + #[cfg(target_env = "msvc")] + pub use self::shims::*; + #[cfg(target_env = "msvc")] + mod shims { + use libc::{c_float, c_int}; + + pub unsafe fn acosf(n: c_float) -> c_float { + f64::acos(n as f64) as c_float + } + + pub unsafe fn asinf(n: c_float) -> c_float { + f64::asin(n as f64) as c_float + } + + pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { + f64::atan2(n as f64, b as f64) as c_float + } + + pub unsafe fn atanf(n: c_float) -> c_float { + f64::atan(n as f64) as c_float + } + + pub unsafe fn coshf(n: c_float) -> c_float { + f64::cosh(n as f64) as c_float + } + + pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { + let (a, b) = f64::frexp(x as f64); + *value = b as c_int; + a as c_float + } + + pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { + f64::ldexp(x as f64, n as isize) as c_float + } + + pub unsafe fn sinhf(n: c_float) -> c_float { + f64::sinh(n as f64) as c_float + } - #[cfg(all(windows, target_env = "msvc"))] - pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { - let (a, b) = f64::frexp(x as f64); - *value = b as c_int; - a as c_float + pub unsafe fn tanf(n: c_float) -> c_float { + f64::tan(n as f64) as c_float + } + + pub unsafe fn tanhf(n: c_float) -> c_float { + f64::tanh(n as f64) as c_float + } } } @@ -761,7 +802,13 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn sin(self) -> f32 { - unsafe { intrinsics::sinf32(self) } + return sinf(self); + + // see notes in `core::f32::Float::floor` + #[cfg(target_env = "msvc")] + fn sinf(f: f32) -> f32 { (f as f64).sin() as f32 } + #[cfg(not(target_env = "msvc"))] + fn sinf(f: f32) -> f32 { unsafe { intrinsics::sinf32(f) } } } /// Computes the cosine of a number (in radians). @@ -778,7 +825,13 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn cos(self) -> f32 { - unsafe { intrinsics::cosf32(self) } + return cosf(self); + + // see notes in `core::f32::Float::floor` + #[cfg(target_env = "msvc")] + fn cosf(f: f32) -> f32 { (f as f64).cos() as f32 } + #[cfg(not(target_env = "msvc"))] + fn cosf(f: f32) -> f32 { unsafe { intrinsics::cosf32(f) } } } /// Computes the tangent of a number (in radians). From 10b103af48368c5df644fa61dc417a36083922c8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 26 Jun 2015 09:30:35 -0700 Subject: [PATCH 4/4] std: Fix Windows XP compatibility This commit enables executables linked against the standard library to run on Windows XP. There are two main components of this commit: * APIs not available on XP are shimmed to have a fallback implementation and use runtime detection to determine if they are available. * Mutexes on Windows were reimplemented to use critical sections on XP where rwlocks are not available. The APIs which are not available on XP are: * SetFileInformationByHandle - this is just used by `File::truncate` and that function just returns an error now. * SetThreadStackGuarantee - this is used by the stack overflow support on windows, but if this isn't available then it's just ignored (it seems non-critical). * All condition variable APIs are missing - the shims added for these apis simply always panic for now. We may eventually provide a fallback implementation, but for now the standard library does not rely on condition variables for normal use. * RWLocks, like condition variables, are missing entirely. The same story for condition variables is taken here. These APIs are all now panicking stubs as the standard library doesn't rely on RWLocks for normal use. Currently, as an optimization, we use SRWLOCKs for the standard `sync::Mutex` implementation on Windows, which is indeed required for normal operation of the standard library. To allow the standard library to run on XP, this commit reimplements mutexes on Windows to use SRWLOCK instances *if available* and otherwise a CriticalSection is used (with some checking for recursive locking). With all these changes put together, a 32-bit MSVC-built executable can run on Windows XP and print "hello world" Closes #12842 Closes #19992 Closes #24776 --- src/libstd/dynamic_lib.rs | 2 +- src/libstd/sys/windows/c.rs | 211 +++++++++++------------ src/libstd/sys/windows/compat.rs | 88 ++++++++++ src/libstd/sys/windows/condvar.rs | 28 +-- src/libstd/sys/windows/fs.rs | 8 +- src/libstd/sys/windows/mod.rs | 3 +- src/libstd/sys/windows/mutex.rs | 159 +++++++++++++---- src/libstd/sys/windows/rwlock.rs | 18 +- src/libstd/sys/windows/stack_overflow.rs | 19 +- src/libstd/sys/windows/sync.rs | 60 ------- 10 files changed, 353 insertions(+), 243 deletions(-) create mode 100644 src/libstd/sys/windows/compat.rs delete mode 100644 src/libstd/sys/windows/sync.rs diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index ddafe416305e1..a17d121e60a2a 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -263,7 +263,7 @@ mod dl { use sys::os; use os::windows::prelude::*; use ptr; - use sys::c::compat::kernel32::SetThreadErrorMode; + use sys::c::SetThreadErrorMode; pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> { // disable "dll load failed" error dialog. diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 3c9b2ef1b986e..7f89ea979391e 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -13,6 +13,9 @@ #![allow(bad_style, dead_code, overflowing_literals)] use libc; +use libc::{c_uint, c_ulong}; +use libc::{DWORD, BOOL, BOOLEAN, ERROR_CALL_NOT_IMPLEMENTED, LPVOID, HANDLE}; +use libc::{LPCWSTR, LONG}; pub use self::GET_FILEEX_INFO_LEVELS::*; pub use self::FILE_INFO_BY_HANDLE_CLASS::*; @@ -240,7 +243,32 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER { pub PathBuffer: libc::WCHAR, } +pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE; +pub type PSRWLOCK = *mut SRWLOCK; +pub type ULONG = c_ulong; +pub type ULONG_PTR = c_ulong; + +#[repr(C)] +pub struct CONDITION_VARIABLE { pub ptr: LPVOID } +#[repr(C)] +pub struct SRWLOCK { pub ptr: LPVOID } +#[repr(C)] +pub struct CRITICAL_SECTION { + CriticalSectionDebug: LPVOID, + LockCount: LONG, + RecursionCount: LONG, + OwningThread: HANDLE, + LockSemaphore: HANDLE, + SpinCount: ULONG_PTR +} + +pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { + ptr: 0 as *mut _, +}; +pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ }; + #[link(name = "ws2_32")] +#[link(name = "userenv")] extern "system" { pub fn WSAStartup(wVersionRequested: libc::WORD, lpWSAData: LPWSADATA) -> libc::c_int; @@ -295,115 +323,13 @@ extern "system" { pub fn CancelIo(hFile: libc::HANDLE) -> libc::BOOL; pub fn CancelIoEx(hFile: libc::HANDLE, lpOverlapped: libc::LPOVERLAPPED) -> libc::BOOL; -} - -pub mod compat { - use prelude::v1::*; - use ffi::CString; - use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; - use sync::atomic::{AtomicUsize, Ordering}; + pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION); + pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION); + pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN; + pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION); + pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION); - extern "system" { - fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; - fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID; - } - - fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, - fallback: usize) -> usize { - let mut module: Vec = module.utf16_units().collect(); - module.push(0); - let symbol = CString::new(symbol).unwrap(); - let func = unsafe { - let handle = GetModuleHandleW(module.as_ptr()); - GetProcAddress(handle, symbol.as_ptr()) as usize - }; - let value = if func == 0 {fallback} else {func}; - ptr.store(value, Ordering::SeqCst); - value - } - - /// Macro for creating a compatibility fallback for a Windows function - /// - /// # Examples - /// ``` - /// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) { - /// // Fallback implementation - /// }) - /// ``` - /// - /// Note that arguments unused by the fallback implementation should not be - /// called `_` as they are used to be passed to the real function if - /// available. - macro_rules! compat_fn { - ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) - -> $rettype:ty { $fallback:expr }) => ( - #[inline(always)] - pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { - use sync::atomic::{AtomicUsize, Ordering}; - use mem; - - static PTR: AtomicUsize = AtomicUsize::new(0); - - fn load() -> usize { - ::sys::c::compat::store_func(&PTR, - stringify!($module), - stringify!($symbol), - fallback as usize) - } - - extern "system" fn fallback($($argname: $argtype),*) - -> $rettype { $fallback } - - let addr = match PTR.load(Ordering::SeqCst) { - 0 => load(), - n => n, - }; - let f: extern "system" fn($($argtype),*) -> $rettype = - mem::transmute(addr); - f($($argname),*) - } - ) - } - - /// Compatibility layer for functions in `kernel32.dll` - /// - /// Latest versions of Windows this is needed for: - /// - /// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003 - /// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003 - pub mod kernel32 { - use libc::c_uint; - use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE}; - use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED; - use sys::c::SetLastError; - - compat_fn! { - kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, - _lpTargetFileName: LPCWSTR, - _dwFlags: DWORD) -> BOOLEAN { - unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 } - } - } - - compat_fn! { - kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE, - _lpszFilePath: LPCWSTR, - _cchFilePath: DWORD, - _dwFlags: DWORD) -> DWORD { - unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 } - } - } - - compat_fn! { - kernel32::SetThreadErrorMode(_dwNewMode: DWORD, _lpOldMode: *mut DWORD) -> c_uint { - unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 } - } - } - } -} - -extern "system" { // FIXME - pInputControl should be PCONSOLE_READCONSOLE_CONTROL pub fn ReadConsoleW(hConsoleInput: libc::HANDLE, lpBuffer: libc::LPVOID, @@ -447,10 +373,6 @@ extern "system" { lpCreationTime: *const libc::FILETIME, lpLastAccessTime: *const libc::FILETIME, lpLastWriteTime: *const libc::FILETIME) -> libc::BOOL; - pub fn SetFileInformationByHandle(hFile: libc::HANDLE, - FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - lpFileInformation: libc::LPVOID, - dwBufferSize: libc::DWORD) -> libc::BOOL; pub fn GetTempPathW(nBufferLength: libc::DWORD, lpBuffer: libc::LPCWSTR) -> libc::DWORD; pub fn OpenProcessToken(ProcessHandle: libc::HANDLE, @@ -483,11 +405,70 @@ extern "system" { pub fn SwitchToThread() -> libc::BOOL; pub fn Sleep(dwMilliseconds: libc::DWORD); pub fn GetProcessId(handle: libc::HANDLE) -> libc::DWORD; -} - -#[link(name = "userenv")] -extern "system" { pub fn GetUserProfileDirectoryW(hToken: libc::HANDLE, lpProfileDir: libc::LPCWSTR, lpcchSize: *mut libc::DWORD) -> libc::BOOL; } + +// Functions that aren't available on Windows XP, but we still use them and just +// provide some form of a fallback implementation. +compat_fn! { + kernel32: + + pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, + _lpTargetFileName: LPCWSTR, + _dwFlags: DWORD) -> BOOLEAN { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn GetFinalPathNameByHandleW(_hFile: HANDLE, + _lpszFilePath: LPCWSTR, + _cchFilePath: DWORD, + _dwFlags: DWORD) -> DWORD { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn SetThreadErrorMode(_dwNewMode: DWORD, + _lpOldMode: *mut DWORD) -> c_uint { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn SetFileInformationByHandle(_hFile: HANDLE, + _FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, + _lpFileInformation: LPVOID, + _dwBufferSize: DWORD) -> BOOL { + SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 + } + pub fn SleepConditionVariableSRW(ConditionVariable: PCONDITION_VARIABLE, + SRWLock: PSRWLOCK, + dwMilliseconds: DWORD, + Flags: ULONG) -> BOOL { + panic!("condition variables not available") + } + pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE) + -> () { + panic!("condition variables not available") + } + pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE) + -> () { + panic!("condition variables not available") + } + pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> () { + panic!("rwlocks not available") + } + pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK) -> () { + panic!("rwlocks not available") + } + pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK) -> () { + panic!("rwlocks not available") + } + pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK) -> () { + panic!("rwlocks not available") + } + pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN { + panic!("rwlocks not available") + } + pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN { + panic!("rwlocks not available") + } +} diff --git a/src/libstd/sys/windows/compat.rs b/src/libstd/sys/windows/compat.rs new file mode 100644 index 0000000000000..3a03b91f24ed3 --- /dev/null +++ b/src/libstd/sys/windows/compat.rs @@ -0,0 +1,88 @@ +// Copyright 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. + +//! A "compatibility layer" for spanning XP and Windows 7 +//! +//! The standard library currently binds many functions that are not available +//! on Windows XP, but we would also like to support building executables that +//! run on XP. To do this we specify all non-XP APIs as having a fallback +//! implementation to do something reasonable. +//! +//! This dynamic runtime detection of whether a function is available is +//! implemented with `GetModuleHandle` and `GetProcAddress` paired with a +//! static-per-function which caches the result of the first check. In this +//! manner we pay a semi-large one-time cost up front for detecting whether a +//! function is available but afterwards it's just a load and a jump. + +use prelude::v1::*; + +use ffi::CString; +use libc::{LPVOID, LPCWSTR, HMODULE, LPCSTR}; +use sync::atomic::{AtomicUsize, Ordering}; + +extern "system" { + fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; + fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID; +} + +pub fn lookup(module: &str, symbol: &str) -> Option { + let mut module: Vec = module.utf16_units().collect(); + module.push(0); + let symbol = CString::new(symbol).unwrap(); + unsafe { + let handle = GetModuleHandleW(module.as_ptr()); + match GetProcAddress(handle, symbol.as_ptr()) as usize { + 0 => None, + n => Some(n), + } + } +} + +pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, + fallback: usize) -> usize { + let value = lookup(module, symbol).unwrap_or(fallback); + ptr.store(value, Ordering::SeqCst); + value +} + +macro_rules! compat_fn { + ($module:ident: $( + pub fn $symbol:ident($($argname:ident: $argtype:ty),*) + -> $rettype:ty { + $($body:expr);* + } + )*) => ($( + #[allow(unused_variables)] + pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { + use sync::atomic::{AtomicUsize, Ordering}; + use mem; + type F = unsafe extern "system" fn($($argtype),*) -> $rettype; + + static PTR: AtomicUsize = AtomicUsize::new(0); + + fn load() -> usize { + ::sys::compat::store_func(&PTR, + stringify!($module), + stringify!($symbol), + fallback as usize) + } + unsafe extern "system" fn fallback($($argname: $argtype),*) + -> $rettype { + $($body);* + } + + let addr = match PTR.load(Ordering::SeqCst) { + 0 => load(), + n => n, + }; + mem::transmute::(addr)($($argname),*) + } + )*) +} diff --git a/src/libstd/sys/windows/condvar.rs b/src/libstd/sys/windows/condvar.rs index baa7d1ceea331..04d62200e9bcc 100644 --- a/src/libstd/sys/windows/condvar.rs +++ b/src/libstd/sys/windows/condvar.rs @@ -12,35 +12,35 @@ use prelude::v1::*; use cell::UnsafeCell; use libc::{self, DWORD}; -use sys::os; +use sys::c; use sys::mutex::{self, Mutex}; -use sys::sync as ffi; +use sys::os; use time::Duration; -pub struct Condvar { inner: UnsafeCell } +pub struct Condvar { inner: UnsafeCell } unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} impl Condvar { pub const fn new() -> Condvar { - Condvar { inner: UnsafeCell::new(ffi::CONDITION_VARIABLE_INIT) } + Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) } } #[inline] pub unsafe fn wait(&self, mutex: &Mutex) { - let r = ffi::SleepConditionVariableSRW(self.inner.get(), - mutex::raw(mutex), - libc::INFINITE, - 0); + let r = c::SleepConditionVariableSRW(self.inner.get(), + mutex::raw(mutex), + libc::INFINITE, + 0); debug_assert!(r != 0); } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let r = ffi::SleepConditionVariableSRW(self.inner.get(), - mutex::raw(mutex), - super::dur2timeout(dur), - 0); + let r = c::SleepConditionVariableSRW(self.inner.get(), + mutex::raw(mutex), + super::dur2timeout(dur), + 0); if r == 0 { const ERROR_TIMEOUT: DWORD = 0x5B4; debug_assert_eq!(os::errno() as usize, ERROR_TIMEOUT as usize); @@ -52,12 +52,12 @@ impl Condvar { #[inline] pub unsafe fn notify_one(&self) { - ffi::WakeConditionVariable(self.inner.get()) + c::WakeConditionVariable(self.inner.get()) } #[inline] pub unsafe fn notify_all(&self) { - ffi::WakeAllConditionVariable(self.inner.get()) + c::WakeAllConditionVariable(self.inner.get()) } pub unsafe fn destroy(&self) { diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 437b2cc649117..36fabe72aa0c1 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -497,12 +497,11 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { } pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> { - use sys::c::compat::kernel32::CreateSymbolicLinkW; let src = to_utf16(src); let dst = to_utf16(dst); let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 }; try!(cvt(unsafe { - CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL + c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL })); Ok(()) } @@ -565,14 +564,13 @@ pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { } pub fn canonicalize(p: &Path) -> io::Result { - use sys::c::compat::kernel32::GetFinalPathNameByHandleW; let mut opts = OpenOptions::new(); opts.read(true); let f = try!(File::open(p, &opts)); super::fill_utf16_buf(|buf, sz| unsafe { - GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, - libc::VOLUME_NAME_DOS) + c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, + libc::VOLUME_NAME_DOS) }, |buf| { PathBuf::from(OsString::from_wide(buf)) }) diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 18c8add17a6d6..b6d080109df05 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -22,6 +22,8 @@ use os::windows::ffi::{OsStrExt, OsStringExt}; use path::PathBuf; use time::Duration; +#[macro_use] pub mod compat; + pub mod backtrace; pub mod c; pub mod condvar; @@ -36,7 +38,6 @@ pub mod pipe; pub mod process; pub mod rwlock; pub mod stack_overflow; -pub mod sync; pub mod thread; pub mod thread_local; pub mod time; diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs index 29e370698ad74..277c3d14c0ec5 100644 --- a/src/libstd/sys/windows/mutex.rs +++ b/src/libstd/sys/windows/mutex.rs @@ -8,57 +8,154 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! System Mutexes +//! +//! The Windows implementation of mutexes is a little odd and it may not be +//! immediately obvious what's going on. The primary oddness is that SRWLock is +//! used instead of CriticalSection, and this is done because: +//! +//! 1. SRWLock is several times faster than CriticalSection according to +//! benchmarks performed on both Windows 8 and Windows 7. +//! +//! 2. CriticalSection allows recursive locking while SRWLock deadlocks. The +//! Unix implementation deadlocks so consistency is preferred. See #19962 for +//! more details. +//! +//! 3. While CriticalSection is fair and SRWLock is not, the current Rust policy +//! is there there are no guarantees of fairness. +//! +//! The downside of this approach, however, is that SRWLock is not available on +//! Windows XP, so we continue to have a fallback implementation where +//! CriticalSection is used and we keep track of who's holding the mutex to +//! detect recursive locks. + use prelude::v1::*; use cell::UnsafeCell; -use sys::sync as ffi; use mem; +use sync::atomic::{AtomicUsize, Ordering}; +use sys::c; +use sys::compat; -pub struct Mutex { inner: UnsafeCell } +pub struct Mutex { + lock: AtomicUsize, + held: UnsafeCell, +} unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} -#[inline] -pub unsafe fn raw(m: &Mutex) -> ffi::PSRWLOCK { - m.inner.get() +#[derive(Clone, Copy)] +enum Kind { + SRWLock = 1, + CriticalSection = 2, } -// So you might be asking why we're using SRWLock instead of CriticalSection? -// -// 1. SRWLock is several times faster than CriticalSection according to -// benchmarks performed on both Windows 8 and Windows 7. -// -// 2. CriticalSection allows recursive locking while SRWLock deadlocks. The Unix -// implementation deadlocks so consistency is preferred. See #19962 for more -// details. -// -// 3. While CriticalSection is fair and SRWLock is not, the current Rust policy -// is there there are no guarantees of fairness. +#[inline] +pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { + debug_assert!(mem::size_of::() <= mem::size_of_val(&m.lock)); + &m.lock as *const _ as *mut _ +} impl Mutex { pub const fn new() -> Mutex { - Mutex { inner: UnsafeCell::new(ffi::SRWLOCK_INIT) } + Mutex { + lock: AtomicUsize::new(0), + held: UnsafeCell::new(false), + } } - #[inline] pub unsafe fn lock(&self) { - ffi::AcquireSRWLockExclusive(self.inner.get()) + match kind() { + Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)), + Kind::CriticalSection => { + let re = self.remutex(); + (*re).lock(); + if !self.flag_locked() { + (*re).unlock(); + panic!("cannot recursively lock a mutex"); + } + } + } } - #[inline] pub unsafe fn try_lock(&self) -> bool { - ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0 + match kind() { + Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0, + Kind::CriticalSection => { + let re = self.remutex(); + if !(*re).try_lock() { + false + } else if self.flag_locked() { + true + } else { + (*re).unlock(); + false + } + } + } } - #[inline] pub unsafe fn unlock(&self) { - ffi::ReleaseSRWLockExclusive(self.inner.get()) + *self.held.get() = false; + match kind() { + Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)), + Kind::CriticalSection => (*self.remutex()).unlock(), + } } - #[inline] pub unsafe fn destroy(&self) { - // ... + match kind() { + Kind::SRWLock => {} + Kind::CriticalSection => { + match self.lock.load(Ordering::SeqCst) { + 0 => {} + n => { Box::from_raw(n as *mut ReentrantMutex).destroy(); } + } + } + } + } + + unsafe fn remutex(&self) -> *mut ReentrantMutex { + match self.lock.load(Ordering::SeqCst) { + 0 => {} + n => return n as *mut _, + } + let mut re = Box::new(ReentrantMutex::uninitialized()); + re.init(); + let re = Box::into_raw(re); + match self.lock.compare_and_swap(0, re as usize, Ordering::SeqCst) { + 0 => re, + n => { Box::from_raw(re).destroy(); n as *mut _ } + } + } + + unsafe fn flag_locked(&self) -> bool { + if *self.held.get() { + false + } else { + *self.held.get() = true; + true + } + } } -pub struct ReentrantMutex { inner: UnsafeCell } +fn kind() -> Kind { + static KIND: AtomicUsize = AtomicUsize::new(0); + + let val = KIND.load(Ordering::SeqCst); + if val == Kind::SRWLock as usize { + return Kind::SRWLock + } else if val == Kind::CriticalSection as usize { + return Kind::CriticalSection + } + + let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") { + None => Kind::CriticalSection, + Some(..) => Kind::SRWLock, + }; + KIND.store(ret as usize, Ordering::SeqCst); + return ret; +} + +pub struct ReentrantMutex { inner: UnsafeCell } unsafe impl Send for ReentrantMutex {} unsafe impl Sync for ReentrantMutex {} @@ -69,23 +166,23 @@ impl ReentrantMutex { } pub unsafe fn init(&mut self) { - ffi::InitializeCriticalSection(self.inner.get()); + c::InitializeCriticalSection(self.inner.get()); } pub unsafe fn lock(&self) { - ffi::EnterCriticalSection(self.inner.get()); + c::EnterCriticalSection(self.inner.get()); } #[inline] pub unsafe fn try_lock(&self) -> bool { - ffi::TryEnterCriticalSection(self.inner.get()) != 0 + c::TryEnterCriticalSection(self.inner.get()) != 0 } pub unsafe fn unlock(&self) { - ffi::LeaveCriticalSection(self.inner.get()); + c::LeaveCriticalSection(self.inner.get()); } pub unsafe fn destroy(&self) { - ffi::DeleteCriticalSection(self.inner.get()); + c::DeleteCriticalSection(self.inner.get()); } } diff --git a/src/libstd/sys/windows/rwlock.rs b/src/libstd/sys/windows/rwlock.rs index e727638e3e9b5..25865286db051 100644 --- a/src/libstd/sys/windows/rwlock.rs +++ b/src/libstd/sys/windows/rwlock.rs @@ -11,40 +11,40 @@ use prelude::v1::*; use cell::UnsafeCell; -use sys::sync as ffi; +use sys::c; -pub struct RWLock { inner: UnsafeCell } +pub struct RWLock { inner: UnsafeCell } unsafe impl Send for RWLock {} unsafe impl Sync for RWLock {} impl RWLock { pub const fn new() -> RWLock { - RWLock { inner: UnsafeCell::new(ffi::SRWLOCK_INIT) } + RWLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) } } #[inline] pub unsafe fn read(&self) { - ffi::AcquireSRWLockShared(self.inner.get()) + c::AcquireSRWLockShared(self.inner.get()) } #[inline] pub unsafe fn try_read(&self) -> bool { - ffi::TryAcquireSRWLockShared(self.inner.get()) != 0 + c::TryAcquireSRWLockShared(self.inner.get()) != 0 } #[inline] pub unsafe fn write(&self) { - ffi::AcquireSRWLockExclusive(self.inner.get()) + c::AcquireSRWLockExclusive(self.inner.get()) } #[inline] pub unsafe fn try_write(&self) -> bool { - ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0 + c::TryAcquireSRWLockExclusive(self.inner.get()) != 0 } #[inline] pub unsafe fn read_unlock(&self) { - ffi::ReleaseSRWLockShared(self.inner.get()) + c::ReleaseSRWLockShared(self.inner.get()) } #[inline] pub unsafe fn write_unlock(&self) { - ffi::ReleaseSRWLockExclusive(self.inner.get()) + c::ReleaseSRWLockExclusive(self.inner.get()) } #[inline] diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs index 79b7de4f341ac..cf827848db5c9 100644 --- a/src/libstd/sys/windows/stack_overflow.rs +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rt::util::report_overflow; use core::prelude::*; -use ptr; -use mem; + +use libc::types::os::arch::extra::{LPVOID, DWORD, LONG}; use libc; -use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; +use mem; +use ptr; +use rt::util::report_overflow; +use sys::c; use sys_common::stack; pub struct Handler { @@ -69,8 +71,12 @@ pub unsafe fn cleanup() { } pub unsafe fn make_handler() -> Handler { - if SetThreadStackGuarantee(&mut 0x5000) == 0 { - panic!("failed to reserve stack space for exception handling"); + // This API isn't available on XP, so don't panic in that case and just pray + // it works out ok. + if c::SetThreadStackGuarantee(&mut 0x5000) == 0 { + if libc::GetLastError() as u32 != libc::ERROR_CALL_NOT_IMPLEMENTED as u32 { + panic!("failed to reserve stack space for exception handling"); + } } Handler { _data: 0 as *mut libc::c_void } @@ -103,5 +109,4 @@ extern "system" { fn AddVectoredExceptionHandler(FirstHandler: ULONG, VectoredHandler: PVECTORED_EXCEPTION_HANDLER) -> LPVOID; - fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL; } diff --git a/src/libstd/sys/windows/sync.rs b/src/libstd/sys/windows/sync.rs deleted file mode 100644 index 5410259540eac..0000000000000 --- a/src/libstd/sys/windows/sync.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 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 libc::{BOOL, DWORD, LPVOID, LONG, HANDLE, c_ulong}; -use libc::types::os::arch::extra::BOOLEAN; - -pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE; -pub type PSRWLOCK = *mut SRWLOCK; -pub type ULONG = c_ulong; -pub type ULONG_PTR = c_ulong; - -#[repr(C)] -pub struct CONDITION_VARIABLE { pub ptr: LPVOID } -#[repr(C)] -pub struct SRWLOCK { pub ptr: LPVOID } -#[repr(C)] -pub struct CRITICAL_SECTION { - CriticalSectionDebug: LPVOID, - LockCount: LONG, - RecursionCount: LONG, - OwningThread: HANDLE, - LockSemaphore: HANDLE, - SpinCount: ULONG_PTR -} - -pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { - ptr: 0 as *mut _, -}; -pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ }; - -extern "system" { - // condition variables - pub fn SleepConditionVariableSRW(ConditionVariable: PCONDITION_VARIABLE, - SRWLock: PSRWLOCK, - dwMilliseconds: DWORD, - Flags: ULONG) -> BOOL; - pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE); - pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE); - - // slim rwlocks - pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK); - pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK); - pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK); - pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK); - pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN; - pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN; - - pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION); - pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION); - pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN; - pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION); - pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION); -}