Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add s390x support #36369

Merged
merged 1 commit into from
Sep 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,10 @@ case $CFG_CPUTYPE in
CFG_CPUTYPE=powerpc64le
;;

s390x)
CFG_CPUTYPE=s390x
;;

x86_64 | x86-64 | x64 | amd64)
CFG_CPUTYPE=x86_64
;;
Expand Down
25 changes: 24 additions & 1 deletion mk/cfg/s390x-unknown-linux-gnu.mk
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
# rustbuild-only target
# s390x-unknown-linux-gnu configuration
CROSS_PREFIX_s390x-unknown-linux-gnu=s390x-linux-gnu-
CC_s390x-unknown-linux-gnu=$(CC)
CXX_s390x-unknown-linux-gnu=$(CXX)
CPP_s390x-unknown-linux-gnu=$(CPP)
AR_s390x-unknown-linux-gnu=$(AR)
CFG_LIB_NAME_s390x-unknown-linux-gnu=lib$(1).so
CFG_STATIC_LIB_NAME_s390x-unknown-linux-gnu=lib$(1).a
CFG_LIB_GLOB_s390x-unknown-linux-gnu=lib$(1)-*.so
CFG_LIB_DSYM_GLOB_s390x-unknown-linux-gnu=lib$(1)-*.dylib.dSYM
CFG_CFLAGS_s390x-unknown-linux-gnu := -m64 $(CFLAGS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may need to add -march=z10 to the list of CFLAGS because the gcc we'll be using in the buildbots (the Ubuntu 16.04 one) uses zEC12 as its default arch. Unless the z10 and newer are 100% compatible with the zEC12 instruction set. @uweigand, do you know if that's the case? (We use these CFLAGS to compile C dependencies of std like jemalloc)

@alexcrichton we'll be using rustbuild to build std for this target. Does rustbuild pick up these CFLAGS or do they have to be set somewhere (e.g. in config.toml? or in the bootstrap crate?).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah we'll need to modify gcc-rs to add this flag by default, or it can be added to src/bootstrap/lib.rs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the last few generations of System Z processors are z10, z196, zEC12, and z13. In this order, each more recent processor is 100% upwards compatible with all earlier ones. Or to put it the other way, you can link together objects compiled for any of the processors, as long as you run the resulting binary on the most recent one. Therefore, I don't think it is particularly necessary to force the C components of std to be built for z10.

(The exception might be if you also run into problems with the valgrind tests since valgrind doesn't yet support all zEC12 instructions. That might indeed be a reason to use an older march level, until valgrind is fixed ...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or to put it the other way, you can link together objects compiled for any of the processors, as long as you run the resulting binary on the most recent one. Therefore, I don't think it is particularly necessary to force the C components of std to be built for z10.

Right, in the current state e.g. jemalloc will be compiled for the zEC12 (because that's gcc default arch/cpu level) and Rust code will be compiled for the z10 (because of the target definition) resulting in binaries that only work on zEC12 or newer (at least for std programs, which use/link-to jemalloc by default). Is that OK? I though that the intention of setting the target base cpu to z10 was to support processors as old as the z10?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention was that the Rust upstream source code doesn't force use of a more recent processor on all users. In the context of a particular distribution, where the distro has chosen to require a particular minimum architecture level, then of course that minimum level also applies to Rust binaries (you cannot avoid this anyway, even if you build the C components of std with -march=z10, since it'll still link against libc.so itself, which was built with -march=zEC12 on Ubuntu).

However, if you build Rust on another distro, like RHEL 7 or SLES 12, both of which use a default arch level of z196, you don't want to artificially increase the required architecture level just because of the default level provided as part of the upstream Rust compiler sources.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@uweigand oh one point that may be worth clarify I think @japaric means is that these flags are also used for the binary builds that we ourselves ship. For example for some platforms we disable a bunch of SSE-related things and for others we ratchet down the CPU to be a little older than it is by default. That way the binaries we ship end up being compatible with more systems.

If distros, though, use this same file to build their own binaries then they'll indeed be forced to use older cpus (like z10) by default as it's not easy to change the makefiles. This should be relatively ok, though because:

  • This is only used for jemalloc really, and distros tend to do custom things with that anyway
  • Otherwise we don't really have much C code (especially not perf critical)
  • With rustbuild you should be able to add more flags and override default ones, so it may be able to override this when building for distros.

Does that make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, if you'd shipping binaries yourself, it makes sense to target an older CPU. If the flag to do so needs to be hard-coded in the sources because it's difficult to override, then I guess that's the way is has to be ... In the end, I agree that this shouldn't matter much performance-wise.

B.t.w. if you indeed build binaries to ship that are intended to support multiple distros, then the march level isn't the only problem. You'll also need to watch out for glibc symbol version issues; if you build on Ubuntu, it's possible that the resulting binary won't run on RHEL or SLES since those have much older glibc versions. The general recommendation would be to build binaries on the oldest distro you want to support (in which case also the march problem may be solved as well ...).

CFG_GCCISH_CFLAGS_s390x-unknown-linux-gnu := -g -fPIC -m64 $(CFLAGS)
CFG_GCCISH_CXXFLAGS_s390x-unknown-linux-gnu := -fno-rtti $(CXXFLAGS)
CFG_GCCISH_LINK_FLAGS_s390x-unknown-linux-gnu := -shared -fPIC -ldl -pthread -lrt -g -m64
CFG_GCCISH_DEF_FLAG_s390x-unknown-linux-gnu := -Wl,--export-dynamic,--dynamic-list=
CFG_LLC_FLAGS_s390x-unknown-linux-gnu :=
CFG_INSTALL_NAME_s390x-unknown-linux-gnu =
CFG_EXE_SUFFIX_s390x-unknown-linux-gnu =
CFG_WINDOWSY_s390x-unknown-linux-gnu :=
CFG_UNIXY_s390x-unknown-linux-gnu := 1
CFG_LDPATH_s390x-unknown-linux-gnu :=
CFG_RUN_s390x-unknown-linux-gnu=$(2)
CFG_RUN_TARG_s390x-unknown-linux-gnu=$(call CFG_RUN_s390x-unknown-linux-gnu,,$(2))
CFG_GNU_TRIPLE_s390x-unknown-linux-gnu := s390x-unknown-linux-gnu
3 changes: 2 additions & 1 deletion src/liballoc_jemalloc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ const MIN_ALIGN: usize = 8;
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64",
target_arch = "mips64")))]
target_arch = "mips64",
target_arch = "s390x")))]
const MIN_ALIGN: usize = 16;

// MALLOCX_ALIGN(a) macro
Expand Down
3 changes: 2 additions & 1 deletion src/liballoc_system/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "mips64")))]
target_arch = "mips64",
target_arch = "s390x")))]
const MIN_ALIGN: usize = 16;

#[no_mangle]
Expand Down
3 changes: 3 additions & 0 deletions src/libpanic_unwind/gcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4

#[cfg(target_arch = "s390x")]
const UNWIND_DATA_REG: (i32, i32) = (6, 7); // R6, R7

// The following code is based on GCC's C and C++ personality routines. For reference, see:
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
Expand Down
8 changes: 6 additions & 2 deletions src/librustc_back/target/s390x_unknown_linux_gnu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ use target::{Target, TargetResult};

pub fn target() -> TargetResult {
let mut base = super::linux_base::opts();
// NOTE(zEC12) matches C toolchain
base.cpu = "zEC12".to_string();
// z10 is the oldest CPU supported by LLVM
base.cpu = "z10".to_string();
// FIXME: The data_layout string below and the ABI implementation in
// cabi_s390x.rs are for now hard-coded to assume the no-vector ABI.
// Pass the -vector feature string to LLVM to respect this assumption.
base.features = "-vector".to_string();
base.max_atomic_width = 64;

Ok(Target {
Expand Down
9 changes: 8 additions & 1 deletion src/librustc_trans/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use cabi_arm;
use cabi_aarch64;
use cabi_powerpc;
use cabi_powerpc64;
use cabi_s390x;
use cabi_mips;
use cabi_mips64;
use cabi_asmjs;
Expand Down Expand Up @@ -301,6 +302,9 @@ impl FnType {
let win_x64_gnu = target.target_os == "windows"
&& target.arch == "x86_64"
&& target.target_env == "gnu";
let linux_s390x = target.target_os == "linux"
&& target.arch == "s390x"
&& target.target_env == "gnu";
let rust_abi = match abi {
RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true,
_ => false
Expand All @@ -326,7 +330,9 @@ impl FnType {
if llsize_of_real(ccx, arg.ty) == 0 {
// For some forsaken reason, x86_64-pc-windows-gnu
// doesn't ignore zero-sized struct arguments.
if is_return || rust_abi || !win_x64_gnu {
// The same is true for s390x-unknown-linux-gnu.
if is_return || rust_abi ||
(!win_x64_gnu && !linux_s390x) {
arg.ignore();
}
}
Expand Down Expand Up @@ -511,6 +517,7 @@ impl FnType {
"mips64" => cabi_mips64::compute_abi_info(ccx, self),
"powerpc" => cabi_powerpc::compute_abi_info(ccx, self),
"powerpc64" => cabi_powerpc64::compute_abi_info(ccx, self),
"s390x" => cabi_s390x::compute_abi_info(ccx, self),
"asmjs" => cabi_asmjs::compute_abi_info(ccx, self),
a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a))
}
Expand Down
150 changes: 150 additions & 0 deletions src/librustc_trans/cabi_s390x.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright 2016 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// FIXME: The assumes we're using the non-vector ABI, i.e. compiling
// for a pre-z13 machine or using -mno-vx.

use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector};
use abi::{FnType, ArgType};
use context::CrateContext;
use type_::Type;

use std::cmp;

fn align_up_to(off: usize, a: usize) -> usize {
return (off + a - 1) / a * a;
}

fn align(off: usize, ty: Type) -> usize {
let a = ty_align(ty);
return align_up_to(off, a);
}

fn ty_align(ty: Type) -> usize {
match ty.kind() {
Integer => ((ty.int_width() as usize) + 7) / 8,
Pointer => 8,
Float => 4,
Double => 8,
Struct => {
if ty.is_packed() {
1
} else {
let str_tys = ty.field_types();
str_tys.iter().fold(1, |a, t| cmp::max(a, ty_align(*t)))
}
}
Array => {
let elt = ty.element_type();
ty_align(elt)
}
Vector => ty_size(ty),
_ => bug!("ty_align: unhandled type")
}
}

fn ty_size(ty: Type) -> usize {
match ty.kind() {
Integer => ((ty.int_width() as usize) + 7) / 8,
Pointer => 8,
Float => 4,
Double => 8,
Struct => {
if ty.is_packed() {
let str_tys = ty.field_types();
str_tys.iter().fold(0, |s, t| s + ty_size(*t))
} else {
let str_tys = ty.field_types();
let size = str_tys.iter().fold(0, |s, t| align(s, *t) + ty_size(*t));
align(size, ty)
}
}
Array => {
let len = ty.array_length();
let elt = ty.element_type();
let eltsz = ty_size(elt);
len * eltsz
}
Vector => {
let len = ty.vector_length();
let elt = ty.element_type();
let eltsz = ty_size(elt);
len * eltsz
}
_ => bug!("ty_size: unhandled type")
}
}

fn classify_ret_ty(ccx: &CrateContext, ret: &mut ArgType) {
if is_reg_ty(ret.ty) {
ret.extend_integer_width_to(64);
} else {
ret.make_indirect(ccx);
}
}

fn classify_arg_ty(ccx: &CrateContext, arg: &mut ArgType) {
if arg.ty.kind() == Struct {
fn is_single_fp_element(tys: &[Type]) -> bool {
if tys.len() != 1 {
return false;
}
match tys[0].kind() {
Float | Double => true,
Struct => is_single_fp_element(&tys[0].field_types()),
_ => false
}
}

if is_single_fp_element(&arg.ty.field_types()) {
match ty_size(arg.ty) {
4 => arg.cast = Some(Type::f32(ccx)),
8 => arg.cast = Some(Type::f64(ccx)),
_ => arg.make_indirect(ccx)
}
} else {
match ty_size(arg.ty) {
1 => arg.cast = Some(Type::i8(ccx)),
2 => arg.cast = Some(Type::i16(ccx)),
4 => arg.cast = Some(Type::i32(ccx)),
8 => arg.cast = Some(Type::i64(ccx)),
_ => arg.make_indirect(ccx)
}
}
return;
}

if is_reg_ty(arg.ty) {
arg.extend_integer_width_to(64);
} else {
arg.make_indirect(ccx);
}
}

fn is_reg_ty(ty: Type) -> bool {
match ty.kind() {
Integer
| Pointer
| Float
| Double => ty_size(ty) <= 8,
_ => false
}
}

pub fn compute_abi_info(ccx: &CrateContext, fty: &mut FnType) {
if !fty.ret.is_ignore() {
classify_ret_ty(ccx, &mut fty.ret);
}

for arg in &mut fty.args {
if arg.is_ignore() { continue; }
classify_arg_ty(ccx, arg);
}
}
1 change: 1 addition & 0 deletions src/librustc_trans/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ mod cabi_mips;
mod cabi_mips64;
mod cabi_powerpc;
mod cabi_powerpc64;
mod cabi_s390x;
mod cabi_x86;
mod cabi_x86_64;
mod cabi_x86_win64;
Expand Down
6 changes: 6 additions & 0 deletions src/libstd/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ pub mod consts {
/// - mips64
/// - powerpc
/// - powerpc64
/// - s390x
#[stable(feature = "env", since = "1.0.0")]
pub const ARCH: &'static str = super::arch::ARCH;

Expand Down Expand Up @@ -942,6 +943,11 @@ mod arch {
pub const ARCH: &'static str = "powerpc64";
}

#[cfg(target_arch = "s390x")]
mod arch {
pub const ARCH: &'static str = "s390x";
}

#[cfg(target_arch = "le32")]
mod arch {
pub const ARCH: &'static str = "le32";
Expand Down
5 changes: 5 additions & 0 deletions src/libstd/os/linux/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ mod arch {
pub use libc::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t};
}

#[cfg(target_arch = "s390x")]
mod arch {
pub use libc::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t};
}

#[cfg(target_arch = "aarch64")]
mod arch {
use os::raw::{c_long, c_int};
Expand Down
6 changes: 4 additions & 2 deletions src/libstd/os/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
all(target_os = "linux", any(target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64"))))]
target_arch = "powerpc64",
target_arch = "s390x"))))]
#[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = u8;
#[cfg(not(any(target_os = "android",
target_os = "emscripten",
all(target_os = "linux", any(target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64")))))]
target_arch = "powerpc64",
target_arch = "s390x")))))]
#[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = i8;
#[stable(feature = "raw_os", since = "1.1.0")] pub type c_schar = i8;
#[stable(feature = "raw_os", since = "1.1.0")] pub type c_uchar = u8;
Expand Down
14 changes: 10 additions & 4 deletions src/libstd/sys/unix/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,17 @@ mod imp {
target_arch = "arm",
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "powerpc64")))]
target_arch = "powerpc64",
target_arch = "s390x")))]
fn getrandom(buf: &mut [u8]) -> libc::c_long {
#[cfg(target_arch = "x86_64")]
const NR_GETRANDOM: libc::c_long = 318;
#[cfg(target_arch = "x86")]
const NR_GETRANDOM: libc::c_long = 355;
#[cfg(target_arch = "arm")]
const NR_GETRANDOM: libc::c_long = 384;
#[cfg(target_arch = "s390x")]
const NR_GETRANDOM: libc::c_long = 349;
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
const NR_GETRANDOM: libc::c_long = 359;
#[cfg(target_arch = "aarch64")]
Expand All @@ -71,7 +74,8 @@ mod imp {
target_arch = "arm",
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "powerpc64"))))]
target_arch = "powerpc64",
target_arch = "s390x"))))]
fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

fn getrandom_fill_bytes(v: &mut [u8]) {
Expand Down Expand Up @@ -110,7 +114,8 @@ mod imp {
target_arch = "arm",
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "powerpc64")))]
target_arch = "powerpc64",
target_arch = "s390x")))]
fn is_getrandom_available() -> bool {
use sync::atomic::{AtomicBool, Ordering};
use sync::Once;
Expand Down Expand Up @@ -139,7 +144,8 @@ mod imp {
target_arch = "arm",
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "powerpc64"))))]
target_arch = "powerpc64",
target_arch = "s390x"))))]
fn is_getrandom_available() -> bool { false }

pub struct OsRng {
Expand Down
3 changes: 3 additions & 0 deletions src/libunwind/libunwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ pub const unwinder_private_data_size: usize = 2;
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "s390x")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "asmjs")]
pub const unwinder_private_data_size: usize = 20;

Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/asm-bad-clobber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// ignore-android
// ignore-arm
// ignore-aarch64
// ignore-s390x

#![feature(asm, rustc_attrs)]

Expand Down
Loading