Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3be9476

Browse files
committedFeb 1, 2020
Auto merge of #67502 - Mark-Simulacrum:opt-catch, r=<try>
Optimize catch_unwind to match C++ try/catch This refactors the implementation of catching unwinds to allow LLVM to inline the "try" closure directly into the happy path, avoiding indirection. This means that the catch_unwind implementation is (after this PR) zero-cost unless a panic is thrown. https://rust.godbolt.org/z/cZcUSB is an example of the current codegen in a simple case. Notably, the codegen is *exactly the same* if `-Cpanic=abort` is passed, which is clearly not great. This PR, on the other hand, generates the following assembly: ```asm # -Cpanic=unwind: push rbx mov ebx,0x2a call QWORD PTR [rip+0x1c53c] # <happy> mov eax,ebx pop rbx ret mov rdi,rax call QWORD PTR [rip+0x1c537] # cleanup function call call QWORD PTR [rip+0x1c539] # <unfortunate> mov ebx,0xd mov eax,ebx pop rbx ret # -Cpanic=abort: push rax call QWORD PTR [rip+0x20a1] # <happy> mov eax,0x2a pop rcx ret ``` Fixes #64224, and resolves #64222.
2 parents cd1ef39 + de9bbc4 commit 3be9476

File tree

31 files changed

+322
-227
lines changed

31 files changed

+322
-227
lines changed
 

‎Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -2309,6 +2309,7 @@ dependencies = [
23092309
name = "panic_abort"
23102310
version = "0.0.0"
23112311
dependencies = [
2312+
"cfg-if",
23122313
"compiler_builtins",
23132314
"core",
23142315
"libc",

‎src/ci/azure-pipelines/try.yml

+46-33
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ variables:
66
- group: prod-credentials
77

88
jobs:
9-
- job: Linux
10-
timeoutInMinutes: 600
11-
pool:
12-
vmImage: ubuntu-16.04
13-
steps:
14-
- template: steps/run.yml
15-
strategy:
16-
matrix:
17-
dist-x86_64-linux: {}
18-
dist-x86_64-linux-alt:
19-
IMAGE: dist-x86_64-linux
9+
# - job: Linux
10+
# timeoutInMinutes: 600
11+
# pool:
12+
# vmImage: ubuntu-16.04
13+
# steps:
14+
# - template: steps/run.yml
15+
# strategy:
16+
# matrix:
17+
# dist-x86_64-linux: {}
18+
# dist-x86_64-linux-alt:
19+
# IMAGE: dist-x86_64-linux
2020

2121
# The macOS and Windows builds here are currently disabled due to them not being
2222
# overly necessary on `try` builds. We also don't actually have anything that
@@ -49,25 +49,38 @@ jobs:
4949
# NO_LLVM_ASSERTIONS: 1
5050
# NO_DEBUG_ASSERTIONS: 1
5151
#
52-
# - job: Windows
53-
# timeoutInMinutes: 600
54-
# pool:
55-
# vmImage: 'vs2017-win2016'
56-
# steps:
57-
# - template: steps/run.yml
58-
# strategy:
59-
# matrix:
60-
# dist-x86_64-msvc:
61-
# RUST_CONFIGURE_ARGS: >
62-
# --build=x86_64-pc-windows-msvc
63-
# --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc
64-
# --enable-full-tools
65-
# --enable-profiler
66-
# SCRIPT: python x.py dist
67-
# DIST_REQUIRE_ALL_TOOLS: 1
68-
# DEPLOY: 1
69-
#
70-
# dist-x86_64-msvc-alt:
71-
# RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler
72-
# SCRIPT: python x.py dist
73-
# DEPLOY_ALT: 1
52+
- job: Windows
53+
timeoutInMinutes: 600
54+
pool:
55+
vmImage: 'vs2017-win2016'
56+
steps:
57+
- template: steps/run.yml
58+
strategy:
59+
matrix:
60+
x86_64-mingw-1:
61+
SCRIPT: make ci-mingw-subset-1
62+
RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu
63+
CUSTOM_MINGW: 1
64+
# FIXME(#59637)
65+
NO_DEBUG_ASSERTIONS: 1
66+
NO_LLVM_ASSERTIONS: 1
67+
# x86_64-mingw-2:
68+
# SCRIPT: make ci-mingw-subset-2
69+
# RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu
70+
# CUSTOM_MINGW: 1
71+
# i686-mingw-1:
72+
# RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
73+
# SCRIPT: make ci-mingw-subset-1
74+
# CUSTOM_MINGW: 1
75+
# # FIXME(#59637)
76+
# NO_DEBUG_ASSERTIONS: 1
77+
# NO_LLVM_ASSERTIONS: 1
78+
i686-mingw-2:
79+
RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
80+
SCRIPT: make ci-mingw-subset-2
81+
CUSTOM_MINGW: 1
82+
# dist-i686-mingw:
83+
# RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-full-tools --enable-profiler
84+
# SCRIPT: python x.py dist
85+
# CUSTOM_MINGW: 1
86+
# DIST_REQUIRE_ALL_TOOLS: 1

‎src/doc/unstable-book/src/language-features/lang-items.md

+10-5
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
5252
5353
#[lang = "eh_personality"] extern fn rust_eh_personality() {}
5454
#[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } }
55-
#[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
55+
extern "C" fn eh_unwind_resume(_: *mut u8) -> ! {
56+
loop {}
57+
}
58+
#[lang = "eh_unwind_resume"]
59+
static _RESUME: fn(*mut u8) -> ! = eh_unwind_resume;
5660
#[no_mangle] pub extern fn rust_eh_register_frames () {}
5761
#[no_mangle] pub extern fn rust_eh_unregister_frames () {}
5862
```
@@ -131,10 +135,12 @@ pub extern fn rust_eh_personality() {
131135
}
132136
133137
// This function may be needed based on the compilation target.
134-
#[lang = "eh_unwind_resume"]
135-
#[no_mangle]
136-
pub extern fn rust_eh_unwind_resume() {
138+
extern "C" fn eh_unwind_resume(_: *mut u8) -> ! {
139+
loop {}
137140
}
141+
#[lang = "eh_unwind_resume"]
142+
static _RESUME: fn(*mut u8) -> ! = eh_unwind_resume;
143+
138144
139145
#[lang = "panic_impl"]
140146
#[no_mangle]
@@ -248,7 +254,6 @@ the source code.
248254
- `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)
249255
- `eh_personality`: `libpanic_unwind/seh.rs` (SEH)
250256
- `eh_unwind_resume`: `libpanic_unwind/gcc.rs` (GCC)
251-
- `eh_catch_typeinfo`: `libpanic_unwind/seh.rs` (SEH)
252257
- `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC)
253258
- `panic`: `libcore/panicking.rs`
254259
- `panic_bounds_check`: `libcore/panicking.rs`

‎src/libpanic_abort/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ doc = false
1414
core = { path = "../libcore" }
1515
libc = { version = "0.2", default-features = false }
1616
compiler_builtins = "0.1.0"
17+
cfg-if = "0.1.8"

‎src/libpanic_abort/lib.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,14 @@
1818
#![feature(staged_api)]
1919
#![feature(rustc_attrs)]
2020

21-
// Rust's "try" function, but if we're aborting on panics we just call the
22-
// function as there's nothing else we need to do here.
21+
use core::any::Any;
22+
23+
// We need the definition of TryPayload for __rust_panic_cleanup.
24+
include!("../libpanic_unwind/payload.rs");
25+
2326
#[rustc_std_internal_symbol]
24-
pub unsafe extern "C" fn __rust_maybe_catch_panic(
25-
f: fn(*mut u8),
26-
data: *mut u8,
27-
_data_ptr: *mut usize,
28-
_vtable_ptr: *mut usize,
29-
) -> u32 {
30-
f(data);
31-
0
27+
pub unsafe extern "C" fn __rust_panic_cleanup(_: TryPayload) -> *mut (dyn Any + Send + 'static) {
28+
unreachable!()
3229
}
3330

3431
// "Leak" the payload and shim to the relevant abort on the platform in

‎src/libpanic_unwind/dummy.rs

-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ use alloc::boxed::Box;
66
use core::any::Any;
77
use core::intrinsics;
88

9-
pub fn payload() -> *mut u8 {
10-
core::ptr::null_mut()
11-
}
12-
139
pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
1410
intrinsics::abort()
1511
}

‎src/libpanic_unwind/emcc.rs

-4
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,6 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
4848
name: b"rust_panic\0".as_ptr(),
4949
};
5050

51-
pub fn payload() -> *mut u8 {
52-
ptr::null_mut()
53-
}
54-
5551
struct Exception {
5652
// This needs to be an Option because the object's lifetime follows C++
5753
// semantics: when catch_unwind moves the Box out of the exception it must

‎src/libpanic_unwind/gcc.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848

4949
use alloc::boxed::Box;
5050
use core::any::Any;
51-
use core::ptr;
5251

5352
use crate::dwarf::eh::{self, EHAction, EHContext};
5453
use libc::{c_int, uintptr_t};
@@ -83,10 +82,6 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
8382
}
8483
}
8584

86-
pub fn payload() -> *mut u8 {
87-
ptr::null_mut()
88-
}
89-
9085
pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
9186
let exception = Box::from_raw(ptr as *mut Exception);
9287
exception.cause
@@ -329,16 +324,25 @@ unsafe fn find_eh_action(
329324
eh::find_eh_action(lsda, &eh_context, foreign_exception)
330325
}
331326

332-
// See docs in the `unwind` module.
327+
#[cfg(all(
328+
target_os = "windows",
329+
any(target_arch = "x86", target_arch = "x86_64"),
330+
target_env = "gnu"
331+
))]
332+
#[cfg_attr(not(bootstrap), lang = "eh_unwind_resume")]
333+
#[used]
334+
pub static RESUME: unsafe extern "C" fn(*mut uw::_Unwind_Exception) -> ! =
335+
uw::_Unwind_Resume as unsafe extern "C" fn(_) -> !;
336+
333337
#[cfg(all(
334338
target_os = "windows",
335339
any(target_arch = "x86", target_arch = "x86_64"),
336340
target_env = "gnu"
337341
))]
338342
#[lang = "eh_unwind_resume"]
339-
#[unwind(allowed)]
340-
unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
341-
uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception);
343+
#[cfg(bootstrap)]
344+
pub unsafe extern "C" fn rust_eh_unwind_resume(p: *mut u8) -> ! {
345+
uw::_Unwind_Resume(p as *mut uw::_Unwind_Exception)
342346
}
343347

344348
// Frame unwind info registration

‎src/libpanic_unwind/hermit.rs

-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ use alloc::boxed::Box;
66
use core::any::Any;
77
use core::ptr;
88

9-
pub fn payload() -> *mut u8 {
10-
ptr::null_mut()
11-
}
12-
139
pub unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
1410
extern "C" {
1511
pub fn __rust_abort() -> !;

‎src/libpanic_unwind/lib.rs

+12-25
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,22 @@
2222
#![feature(libc)]
2323
#![feature(nll)]
2424
#![feature(panic_unwind)]
25-
#![feature(raw)]
2625
#![feature(staged_api)]
2726
#![feature(std_internals)]
2827
#![feature(unwind_attributes)]
2928
#![feature(abi_thiscall)]
29+
#![feature(rustc_attrs)]
30+
#![feature(raw)]
3031
#![panic_runtime]
3132
#![feature(panic_runtime)]
33+
#![allow(dead_code)]
3234

3335
use alloc::boxed::Box;
34-
use core::intrinsics;
35-
use core::mem;
36+
use core::any::Any;
3637
use core::panic::BoxMeUp;
37-
use core::raw;
3838

39+
// If adding to this list, you should also look at the list of TryPayload types
40+
// defined in payload.rs and likely add to there as well.
3941
cfg_if::cfg_if! {
4042
if #[cfg(target_os = "emscripten")] {
4143
#[path = "emcc.rs"]
@@ -61,6 +63,8 @@ cfg_if::cfg_if! {
6163
}
6264
}
6365

66+
include!("payload.rs");
67+
6468
extern "C" {
6569
/// Handler in libstd called when a panic object is dropped outside of
6670
/// `catch_unwind`.
@@ -69,28 +73,11 @@ extern "C" {
6973

7074
mod dwarf;
7175

72-
// Entry point for catching an exception, implemented using the `try` intrinsic
73-
// in the compiler.
74-
//
75-
// The interaction between the `payload` function and the compiler is pretty
76-
// hairy and tightly coupled, for more information see the compiler's
77-
// implementation of this.
7876
#[no_mangle]
79-
pub unsafe extern "C" fn __rust_maybe_catch_panic(
80-
f: fn(*mut u8),
81-
data: *mut u8,
82-
data_ptr: *mut usize,
83-
vtable_ptr: *mut usize,
84-
) -> u32 {
85-
let mut payload = imp::payload();
86-
if intrinsics::r#try(f, data, &mut payload as *mut _ as *mut _) == 0 {
87-
0
88-
} else {
89-
let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
90-
*data_ptr = obj.data as usize;
91-
*vtable_ptr = obj.vtable as usize;
92-
1
93-
}
77+
pub unsafe extern "C" fn __rust_panic_cleanup(
78+
payload: TryPayload,
79+
) -> *mut (dyn Any + Send + 'static) {
80+
Box::into_raw(imp::cleanup(payload))
9481
}
9582

9683
// Entry point for raising an exception, just delegates to the platform-specific

‎src/libpanic_unwind/payload.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Type definition for the payload argument of the try intrinsic.
2+
//
3+
// This must be kept in sync with the implementations of the try intrinsic.
4+
//
5+
// This file is included by both panic runtimes and libstd. It is part of the
6+
// panic runtime ABI.
7+
cfg_if::cfg_if! {
8+
if #[cfg(target_os = "emscripten")] {
9+
type TryPayload = *mut u8;
10+
} else if #[cfg(target_arch = "wasm32")] {
11+
type TryPayload = *mut u8;
12+
} else if #[cfg(target_os = "hermit")] {
13+
type TryPayload = *mut u8;
14+
} else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {
15+
type TryPayload = *mut u8;
16+
} else if #[cfg(target_env = "msvc")] {
17+
type TryPayload = [u64; 2];
18+
} else {
19+
type TryPayload = *mut u8;
20+
}
21+
}

‎src/libpanic_unwind/seh.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ pub struct _TypeDescriptor {
167167

168168
// Note that we intentionally ignore name mangling rules here: we don't want C++
169169
// to be able to catch Rust panics by simply declaring a `struct rust_panic`.
170+
//
171+
// When modifying, make sure that the type name string exactly matches
172+
// the one used in src/librustc_codegen_llvm/intrinsic.rs.
170173
const TYPE_NAME: [u8; 11] = *b"rust_panic\0";
171174

172175
static mut THROW_INFO: _ThrowInfo = _ThrowInfo {
@@ -199,12 +202,12 @@ extern "C" {
199202
static TYPE_INFO_VTABLE: *const u8;
200203
}
201204

202-
// We use #[lang = "eh_catch_typeinfo"] here as this is the type descriptor which
203-
// we'll use in LLVM's `catchpad` instruction which ends up also being passed as
204-
// an argument to the C++ personality function.
205+
// This type descriptor is only used when throwing an exception. The catch part
206+
// is handled by the try intrinsic, which generates its own TypeDescriptor.
205207
//
206-
// Again, I'm not entirely sure what this is describing, it just seems to work.
207-
#[cfg_attr(not(test), lang = "eh_catch_typeinfo")]
208+
// This is fine since the MSVC runtime uses string comparison on the type name
209+
// to match TypeDescriptors rather than pointer equality.
210+
#[cfg_attr(bootstrap, lang = "eh_catch_typeinfo")]
208211
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
209212
pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _,
210213
spare: core::ptr::null_mut(),
@@ -315,10 +318,6 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
315318
_CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
316319
}
317320

318-
pub fn payload() -> [u64; 2] {
319-
[0; 2]
320-
}
321-
322321
pub unsafe fn cleanup(payload: [u64; 2]) -> Box<dyn Any + Send> {
323322
mem::transmute(raw::TraitObject { data: payload[0] as *mut _, vtable: payload[1] as *mut _ })
324323
}

‎src/librustc/middle/lang_items.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ language_item_table! {
385385
StartFnLangItem, "start", start_fn, Target::Fn;
386386

387387
EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn;
388-
EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume, Target::Fn;
388+
EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume, Target::Static;
389389
EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static;
390390

391391
OwnedBoxLangItem, "owned_box", owned_box, Target::Struct;

‎src/librustc_codegen_llvm/context.rs

+6-13
Original file line numberDiff line numberDiff line change
@@ -407,17 +407,9 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
407407
let tcx = self.tcx;
408408
assert!(self.sess().target.target.options.custom_unwind_resume);
409409
if let Some(def_id) = tcx.lang_items().eh_unwind_resume() {
410-
let llfn = self.get_fn_addr(
411-
ty::Instance::resolve(
412-
tcx,
413-
ty::ParamEnv::reveal_all(),
414-
def_id,
415-
tcx.intern_substs(&[]),
416-
)
417-
.unwrap(),
418-
);
419-
unwresume.set(Some(llfn));
420-
return llfn;
410+
let static_ptr = self.get_static(def_id);
411+
unwresume.set(Some(static_ptr));
412+
return static_ptr;
421413
}
422414

423415
let sig = ty::Binder::bind(tcx.mk_fn_sig(
@@ -431,8 +423,9 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
431423
let fn_abi = FnAbi::of_fn_ptr(self, sig, &[]);
432424
let llfn = self.declare_fn("rust_eh_unwind_resume", &fn_abi);
433425
attributes::apply_target_cpu_attr(self, llfn);
434-
unwresume.set(Some(llfn));
435-
llfn
426+
let static_ptr = self.static_addr_of(llfn, tcx.data_layout.pointer_align.abi, None);
427+
unwresume.set(Some(static_ptr));
428+
static_ptr
436429
}
437430

438431
fn sess(&self) -> &Session {

‎src/librustc_codegen_llvm/intrinsic.rs

+31-7
Original file line numberDiff line numberDiff line change
@@ -858,8 +858,10 @@ fn try_intrinsic(
858858
) {
859859
if bx.sess().no_landing_pads() {
860860
bx.call(func, &[data], None);
861-
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
862-
bx.store(bx.const_null(bx.type_i8p()), dest, ptr_align);
861+
// Return 0 unconditionally from the intrinsic call;
862+
// we can never unwind.
863+
let ret_align = bx.tcx().data_layout.i32_align.abi;
864+
bx.store(bx.const_i32(0), dest, ret_align);
863865
} else if wants_msvc_seh(bx.sess()) {
864866
codegen_msvc_try(bx, func, data, local_ptr, dest);
865867
} else {
@@ -952,19 +954,39 @@ fn codegen_msvc_try(
952954
let cs = catchswitch.catch_switch(None, None, 1);
953955
catchswitch.add_handler(cs, catchpad.llbb());
954956

957+
// We can't use the TypeDescriptor defined in libpanic_unwind because it
958+
// might be in another DLL and the SEH encoding only supports specifying
959+
// a TypeDescriptor from the current module.
960+
//
961+
// However this isn't an issue since the MSVC runtime uses string
962+
// comparison on the type name to match TypeDescriptors rather than
963+
// pointer equality.
964+
//
965+
// So instead we generate a new TypeDescriptor in each module that uses
966+
// `try` and let the linker merge duplicate definitions in the same
967+
// module.
968+
//
969+
// When modifying, make sure that the type_name string exactly matches
970+
// the one used in src/libpanic_unwind/seh.rs.
971+
let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_i8p());
972+
let type_name = bx.const_bytes(b"rust_panic\0");
973+
let type_info =
974+
bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_i8p()), type_name], false);
975+
let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info));
976+
unsafe {
977+
llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
978+
llvm::SetUniqueComdat(bx.llmod, tydesc);
979+
llvm::LLVMSetInitializer(tydesc, type_info);
980+
}
981+
955982
// The flag value of 8 indicates that we are catching the exception by
956983
// reference instead of by value. We can't use catch by value because
957984
// that requires copying the exception object, which we don't support
958985
// since our exception object effectively contains a Box.
959986
//
960987
// Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
961988
let flags = bx.const_i32(8);
962-
let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() {
963-
Some(did) => bx.get_static(did),
964-
None => bug!("eh_catch_typeinfo not defined, but needed for SEH unwinding"),
965-
};
966989
let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]);
967-
968990
let i64_align = bx.tcx().data_layout.i64_align.abi;
969991
let payload_ptr = catchpad.load(slot, ptr_align);
970992
let payload = catchpad.load(payload_ptr, i64_align);
@@ -1084,6 +1106,8 @@ fn gen_fn<'ll, 'tcx>(
10841106
));
10851107
let fn_abi = FnAbi::of_fn_ptr(cx, rust_fn_sig, &[]);
10861108
let llfn = cx.declare_fn(name, &fn_abi);
1109+
cx.set_frame_pointer_elimination(llfn);
1110+
cx.apply_target_cpu_attr(llfn);
10871111
// FIXME(eddyb) find a nicer way to do this.
10881112
unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
10891113
let bx = Builder::new_block(cx, llfn, "entry-block");

‎src/librustc_codegen_ssa/mir/block.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
184184
lp = bx.insert_value(lp, lp1, 1);
185185
bx.resume(lp);
186186
} else {
187-
bx.call(bx.eh_unwind_resume(), &[lp0], helper.funclet(self));
187+
let llfn = bx.load(bx.eh_unwind_resume(), bx.tcx().data_layout.pointer_align.abi);
188+
bx.call(llfn, &[lp0], helper.funclet(self));
188189
bx.unreachable();
189190
}
190191
}

‎src/libstd/panicking.rs

+27-26
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ use core::panic::{BoxMeUp, Location, PanicInfo};
1212
use crate::any::Any;
1313
use crate::fmt;
1414
use crate::intrinsics;
15-
use crate::mem::{self, ManuallyDrop};
15+
use crate::mem::{self, ManuallyDrop, MaybeUninit};
1616
use crate::process;
17-
use crate::raw;
1817
use crate::sync::atomic::{AtomicBool, Ordering};
1918
use crate::sys::stdio::panic_output;
2019
use crate::sys_common::backtrace::{self, RustBacktrace};
@@ -29,6 +28,9 @@ use crate::io::set_panic;
2928
#[cfg(test)]
3029
use realstd::io::set_panic;
3130

31+
// Include the definition of UnwindPayload from libpanic_unwind.
32+
include!("../libpanic_unwind/payload.rs");
33+
3234
// Binary interface to the panic runtime that the standard library depends on.
3335
//
3436
// The standard library is tagged with `#![needs_panic_runtime]` (introduced in
@@ -41,12 +43,9 @@ use realstd::io::set_panic;
4143
// hook up these functions, but it is not this day!
4244
#[allow(improper_ctypes)]
4345
extern "C" {
44-
fn __rust_maybe_catch_panic(
45-
f: fn(*mut u8),
46-
data: *mut u8,
47-
data_ptr: *mut usize,
48-
vtable_ptr: *mut usize,
49-
) -> u32;
46+
/// The payload ptr here is actually the same as the payload ptr for the try
47+
/// intrinsic (i.e., is really `*mut [u64; 2]` or `*mut *mut u8`).
48+
fn __rust_panic_cleanup(payload: TryPayload) -> *mut (dyn Any + Send + 'static);
5049

5150
/// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings.
5251
/// It cannot be `Box<dyn BoxMeUp>` because the other end of this call does not depend
@@ -250,9 +249,9 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
250249
}
251250

252251
// We do some sketchy operations with ownership here for the sake of
253-
// performance. We can only pass pointers down to
254-
// `__rust_maybe_catch_panic` (can't pass objects by value), so we do all
255-
// the ownership tracking here manually using a union.
252+
// performance. We can only pass pointers down to `do_call` (can't pass
253+
// objects by value), so we do all the ownership tracking here manually
254+
// using a union.
256255
//
257256
// We go through a transition where:
258257
//
@@ -263,7 +262,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
263262
// * If the closure successfully returns, we write the return value into the
264263
// data's return slot. Note that `ptr::write` is used as it's overwriting
265264
// uninitialized data.
266-
// * Finally, when we come back out of the `__rust_maybe_catch_panic` we're
265+
// * Finally, when we come back out of the `try` intrinsic we're
267266
// in one of two states:
268267
//
269268
// 1. The closure didn't panic, in which case the return value was
@@ -274,28 +273,30 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
274273
//
275274
// Once we stack all that together we should have the "most efficient'
276275
// method of calling a catch panic whilst juggling ownership.
277-
let mut any_data = 0;
278-
let mut any_vtable = 0;
279276
let mut data = Data { f: ManuallyDrop::new(f) };
280277

281-
let r = __rust_maybe_catch_panic(
282-
do_call::<F, R>,
283-
&mut data as *mut _ as *mut u8,
284-
&mut any_data,
285-
&mut any_vtable,
286-
);
278+
let mut payload: MaybeUninit<TryPayload> = MaybeUninit::uninit();
287279

288-
return if r == 0 {
280+
let data_ptr = &mut data as *mut _ as *mut u8;
281+
let payload_ptr = payload.as_mut_ptr() as *mut _;
282+
return if intrinsics::r#try(do_call::<F, R>, data_ptr, payload_ptr) == 0 {
289283
debug_assert!(update_panic_count(0) == 0);
290284
Ok(ManuallyDrop::into_inner(data.r))
291285
} else {
286+
Err(cleanup(payload.assume_init()))
287+
};
288+
289+
// We consider unwinding to be rare, so mark this function as cold. However,
290+
// do not mark it no-inline -- that decision is best to leave to the
291+
// optimizer (in most cases this function is not inlined even as a normal,
292+
// non-cold function, though, as of the writing of this comment).
293+
#[cold]
294+
unsafe fn cleanup(payload: TryPayload) -> Box<dyn Any + Send + 'static> {
295+
let obj = Box::from_raw(__rust_panic_cleanup(payload));
292296
update_panic_count(-1);
293297
debug_assert!(update_panic_count(0) == 0);
294-
Err(mem::transmute(raw::TraitObject {
295-
data: any_data as *mut _,
296-
vtable: any_vtable as *mut _,
297-
}))
298-
};
298+
obj
299+
}
299300

300301
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
301302
unsafe {

‎src/libunwind/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![feature(nll)]
55
#![feature(staged_api)]
66
#![feature(unwind_attributes)]
7+
#![feature(lang_items)]
78
#![feature(static_nobundle)]
89
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
910

‎src/test/codegen/catch-unwind.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// compile-flags: -O
2+
3+
#![crate_type = "lib"]
4+
5+
extern "C" {
6+
fn bar();
7+
}
8+
9+
// CHECK-LABEL: @foo
10+
#[no_mangle]
11+
pub unsafe fn foo() -> i32 {
12+
// CHECK: call void @bar
13+
// CHECK: ret i32 0
14+
std::panic::catch_unwind(|| {
15+
bar();
16+
0
17+
})
18+
.unwrap()
19+
}

‎src/test/codegen/try-panic-abort.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-flags: -C panic=abort -O
2+
3+
#![crate_type = "lib"]
4+
#![feature(unwind_attributes, core_intrinsics)]
5+
6+
extern "C" {
7+
#[unwind(allow)]
8+
fn bar(data: *mut u8);
9+
}
10+
11+
// CHECK-LABEL: @foo
12+
#[no_mangle]
13+
pub unsafe fn foo() -> i32 {
14+
// CHECK: call void @bar
15+
// CHECK: ret i32 0
16+
std::intrinsics::r#try(|x| bar(x), 0 as *mut u8, 0 as *mut u8)
17+
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
// no-prefer-dynamic
22

33
#![crate_type = "rlib"]
4-
54
#![no_std]
6-
#![feature(lang_items)]
5+
#![feature(lang_items, core_intrinsics)]
76

87
use core::panic::PanicInfo;
98

109
#[lang = "panic_impl"]
11-
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
10+
fn panic_impl(info: &PanicInfo) -> ! {
11+
loop {}
12+
}
1213
#[lang = "eh_personality"]
1314
fn eh_personality() {}
15+
16+
unsafe extern "C" fn eh_unwind_resume(_: *mut u8) -> ! {
17+
core::intrinsics::abort();
18+
}
1419
#[lang = "eh_unwind_resume"]
15-
fn eh_unwind_resume() {}
20+
static _RESUME: unsafe extern "C" fn(*mut u8) -> ! = eh_unwind_resume;

‎src/test/ui/consts/const-eval/const_panic_libcore_main.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![crate_type = "bin"]
2-
#![feature(lang_items)]
2+
#![feature(lang_items, core_intrinsics)]
33
#![feature(const_panic)]
44
#![no_main]
55
#![no_std]
@@ -17,8 +17,12 @@ const X: () = unimplemented!();
1717

1818
#[lang = "eh_personality"]
1919
fn eh() {}
20+
21+
unsafe extern "C" fn eh_unwind_resume(_: *mut u8) -> ! {
22+
core::intrinsics::abort();
23+
}
2024
#[lang = "eh_unwind_resume"]
21-
fn eh_unwind_resume() {}
25+
static _RESUME: unsafe extern "C" fn(*mut u8) -> ! = eh_unwind_resume;
2226

2327
#[panic_handler]
2428
fn panic(_info: &PanicInfo) -> ! {

‎src/test/ui/macros/macro-comma-behavior.core.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
11
error: 1 positional argument in format string, but no arguments were given
2-
--> $DIR/macro-comma-behavior.rs:21:23
2+
--> $DIR/macro-comma-behavior.rs:36:23
33
|
44
LL | assert_eq!(1, 1, "{}",);
55
| ^^
66

77
error: 1 positional argument in format string, but no arguments were given
8-
--> $DIR/macro-comma-behavior.rs:24:23
8+
--> $DIR/macro-comma-behavior.rs:39:23
99
|
1010
LL | assert_ne!(1, 2, "{}",);
1111
| ^^
1212

1313
error: 1 positional argument in format string, but no arguments were given
14-
--> $DIR/macro-comma-behavior.rs:30:29
14+
--> $DIR/macro-comma-behavior.rs:45:29
1515
|
1616
LL | debug_assert_eq!(1, 1, "{}",);
1717
| ^^
1818

1919
error: 1 positional argument in format string, but no arguments were given
20-
--> $DIR/macro-comma-behavior.rs:33:29
20+
--> $DIR/macro-comma-behavior.rs:48:29
2121
|
2222
LL | debug_assert_ne!(1, 2, "{}",);
2323
| ^^
2424

2525
error: 1 positional argument in format string, but no arguments were given
26-
--> $DIR/macro-comma-behavior.rs:54:19
26+
--> $DIR/macro-comma-behavior.rs:72:19
2727
|
2828
LL | format_args!("{}",);
2929
| ^^
3030

3131
error: 1 positional argument in format string, but no arguments were given
32-
--> $DIR/macro-comma-behavior.rs:72:21
32+
--> $DIR/macro-comma-behavior.rs:92:21
3333
|
3434
LL | unimplemented!("{}",);
3535
| ^^
3636

3737
error: 1 positional argument in format string, but no arguments were given
38-
--> $DIR/macro-comma-behavior.rs:81:24
38+
--> $DIR/macro-comma-behavior.rs:101:24
3939
|
4040
LL | write!(f, "{}",)?;
4141
| ^^

‎src/test/ui/macros/macro-comma-behavior.rs

+31-11
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,29 @@
33
// compile-flags: -C debug_assertions=yes
44
// revisions: std core
55

6-
#![feature(lang_items)]
6+
#![feature(lang_items, core_intrinsics)]
77
#![cfg_attr(core, no_std)]
88

9-
#[cfg(std)] use std::fmt;
10-
#[cfg(core)] use core::fmt;
11-
#[cfg(core)] #[lang = "eh_personality"] fn eh_personality() {}
12-
#[cfg(core)] #[lang = "eh_unwind_resume"] fn eh_unwind_resume() {}
13-
#[cfg(core)] #[lang = "panic_impl"] fn panic_impl(panic: &core::panic::PanicInfo) -> ! { loop {} }
9+
#[cfg(core)]
10+
use core::fmt;
11+
#[cfg(std)]
12+
use std::fmt;
13+
#[cfg(core)]
14+
#[lang = "eh_personality"]
15+
fn eh_personality() {}
16+
#[cfg(core)]
17+
#[lang = "panic_impl"]
18+
fn panic_impl(panic: &core::panic::PanicInfo) -> ! {
19+
loop {}
20+
}
21+
22+
#[cfg(core)]
23+
unsafe extern "C" fn eh_unwind_resume(_: *mut u8) -> ! {
24+
core::intrinsics::abort();
25+
}
26+
#[cfg(core)]
27+
#[lang = "eh_unwind_resume"]
28+
static _RESUME: unsafe extern "C" fn(*mut u8) -> ! = eh_unwind_resume;
1429

1530
// (see documentation of the similarly-named test in run-pass)
1631
fn to_format_or_not_to_format() {
@@ -34,19 +49,22 @@ fn to_format_or_not_to_format() {
3449
//[core]~^ ERROR no arguments
3550
//[std]~^^ ERROR no arguments
3651

37-
#[cfg(std)] {
52+
#[cfg(std)]
53+
{
3854
eprint!("{}",);
3955
//[std]~^ ERROR no arguments
4056
}
4157

42-
#[cfg(std)] {
58+
#[cfg(std)]
59+
{
4360
// FIXME: compile-fail says "expected error not found" even though
4461
// rustc does emit an error
4562
// eprintln!("{}",);
4663
// <DISABLED> [std]~^ ERROR no arguments
4764
}
4865

49-
#[cfg(std)] {
66+
#[cfg(std)]
67+
{
5068
format!("{}",);
5169
//[std]~^ ERROR no arguments
5270
}
@@ -57,12 +75,14 @@ fn to_format_or_not_to_format() {
5775

5876
// if falsum() { panic!("{}",); } // see run-pass
5977

60-
#[cfg(std)] {
78+
#[cfg(std)]
79+
{
6180
print!("{}",);
6281
//[std]~^ ERROR no arguments
6382
}
6483

65-
#[cfg(std)] {
84+
#[cfg(std)]
85+
{
6686
// FIXME: compile-fail says "expected error not found" even though
6787
// rustc does emit an error
6888
// println!("{}",);

‎src/test/ui/macros/macro-comma-behavior.std.stderr

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,59 @@
11
error: 1 positional argument in format string, but no arguments were given
2-
--> $DIR/macro-comma-behavior.rs:21:23
2+
--> $DIR/macro-comma-behavior.rs:36:23
33
|
44
LL | assert_eq!(1, 1, "{}",);
55
| ^^
66

77
error: 1 positional argument in format string, but no arguments were given
8-
--> $DIR/macro-comma-behavior.rs:24:23
8+
--> $DIR/macro-comma-behavior.rs:39:23
99
|
1010
LL | assert_ne!(1, 2, "{}",);
1111
| ^^
1212

1313
error: 1 positional argument in format string, but no arguments were given
14-
--> $DIR/macro-comma-behavior.rs:30:29
14+
--> $DIR/macro-comma-behavior.rs:45:29
1515
|
1616
LL | debug_assert_eq!(1, 1, "{}",);
1717
| ^^
1818

1919
error: 1 positional argument in format string, but no arguments were given
20-
--> $DIR/macro-comma-behavior.rs:33:29
20+
--> $DIR/macro-comma-behavior.rs:48:29
2121
|
2222
LL | debug_assert_ne!(1, 2, "{}",);
2323
| ^^
2424

2525
error: 1 positional argument in format string, but no arguments were given
26-
--> $DIR/macro-comma-behavior.rs:38:18
26+
--> $DIR/macro-comma-behavior.rs:54:18
2727
|
2828
LL | eprint!("{}",);
2929
| ^^
3030

3131
error: 1 positional argument in format string, but no arguments were given
32-
--> $DIR/macro-comma-behavior.rs:50:18
32+
--> $DIR/macro-comma-behavior.rs:68:18
3333
|
3434
LL | format!("{}",);
3535
| ^^
3636

3737
error: 1 positional argument in format string, but no arguments were given
38-
--> $DIR/macro-comma-behavior.rs:54:19
38+
--> $DIR/macro-comma-behavior.rs:72:19
3939
|
4040
LL | format_args!("{}",);
4141
| ^^
4242

4343
error: 1 positional argument in format string, but no arguments were given
44-
--> $DIR/macro-comma-behavior.rs:61:17
44+
--> $DIR/macro-comma-behavior.rs:80:17
4545
|
4646
LL | print!("{}",);
4747
| ^^
4848

4949
error: 1 positional argument in format string, but no arguments were given
50-
--> $DIR/macro-comma-behavior.rs:72:21
50+
--> $DIR/macro-comma-behavior.rs:92:21
5151
|
5252
LL | unimplemented!("{}",);
5353
| ^^
5454

5555
error: 1 positional argument in format string, but no arguments were given
56-
--> $DIR/macro-comma-behavior.rs:81:24
56+
--> $DIR/macro-comma-behavior.rs:101:24
5757
|
5858
LL | write!(f, "{}",)?;
5959
| ^^

‎src/test/ui/no-landing-pads.rs

-23
This file was deleted.

‎src/test/ui/no_owned_box_lang_item.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// error-pattern: requires `owned_box` lang_item
44

5-
#![feature(lang_items, box_syntax)]
5+
#![feature(lang_items, box_syntax, core_intrinsics)]
66
#![no_std]
77

88
use core::panic::PanicInfo;
@@ -11,6 +11,14 @@ fn main() {
1111
let x = box 1i32;
1212
}
1313

14-
#[lang = "eh_personality"] extern fn eh_personality() {}
15-
#[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
16-
#[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} }
14+
#[lang = "eh_personality"]
15+
extern "C" fn eh_personality() {}
16+
unsafe extern "C" fn eh_unwind_resume(_: *mut u8) -> ! {
17+
core::intrinsics::abort();
18+
}
19+
#[lang = "eh_unwind_resume"]
20+
static _RESUME: unsafe extern "C" fn(*mut u8) -> ! = eh_unwind_resume;
21+
#[lang = "panic_impl"]
22+
fn panic_impl(panic: &PanicInfo) -> ! {
23+
loop {}
24+
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
// no-prefer-dynamic
22

33
#![crate_type = "rlib"]
4-
54
#![no_std]
6-
#![feature(lang_items)]
5+
#![feature(lang_items, core_intrinsics)]
76

87
use core::panic::PanicInfo;
98

109
#[lang = "panic_impl"]
11-
fn panic_impl(info: &PanicInfo) -> ! { loop {} }
10+
fn panic_impl(info: &PanicInfo) -> ! {
11+
loop {}
12+
}
1213
#[lang = "eh_personality"]
1314
fn eh_personality() {}
15+
16+
unsafe extern "C" fn eh_unwind_resume(_: *mut u8) -> ! {
17+
core::intrinsics::abort();
18+
}
1419
#[lang = "eh_unwind_resume"]
15-
fn eh_unwind_resume() {}
20+
static _RESUME: unsafe extern "C" fn(*mut u8) -> ! = eh_unwind_resume;

‎src/test/ui/range/issue-54505-no-std.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,25 @@
77
// (so all Ranges resolve to core::ops::Range...)
88

99
#![no_std]
10-
#![feature(lang_items)]
10+
#![feature(core_intrinsics, lang_items)]
1111

1212
use core::ops::RangeBounds;
1313

1414
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
1515
#[lang = "eh_personality"]
16-
extern fn eh_personality() {}
16+
extern "C" fn eh_personality() {}
1717

18+
#[cfg(target_os = "windows")]
19+
extern "C" fn eh_unwind_resume(_: *mut u8) -> ! {
20+
core::intrinsics::abort();
21+
}
1822
#[cfg(target_os = "windows")]
1923
#[lang = "eh_unwind_resume"]
20-
extern fn eh_unwind_resume() {}
21-
24+
static _RESUME: fn(*mut u8) -> ! = eh_unwind_resume;
2225

2326
// take a reference to any built-in range
2427
fn take_range(_r: &impl RangeBounds<i8>) {}
2528

26-
2729
fn main() {
2830
take_range(0..1);
2931
//~^ ERROR mismatched types [E0308]

‎src/test/ui/range/issue-54505-no-std.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error: `#[panic_handler]` function required, but not found
22

33
error[E0308]: mismatched types
4-
--> $DIR/issue-54505-no-std.rs:28:16
4+
--> $DIR/issue-54505-no-std.rs:30:16
55
|
66
LL | take_range(0..1);
77
| ^^^^
@@ -13,7 +13,7 @@ LL | take_range(0..1);
1313
found struct `core::ops::Range<{integer}>`
1414

1515
error[E0308]: mismatched types
16-
--> $DIR/issue-54505-no-std.rs:33:16
16+
--> $DIR/issue-54505-no-std.rs:35:16
1717
|
1818
LL | take_range(1..);
1919
| ^^^
@@ -25,7 +25,7 @@ LL | take_range(1..);
2525
found struct `core::ops::RangeFrom<{integer}>`
2626

2727
error[E0308]: mismatched types
28-
--> $DIR/issue-54505-no-std.rs:38:16
28+
--> $DIR/issue-54505-no-std.rs:40:16
2929
|
3030
LL | take_range(..);
3131
| ^^
@@ -37,7 +37,7 @@ LL | take_range(..);
3737
found struct `core::ops::RangeFull`
3838

3939
error[E0308]: mismatched types
40-
--> $DIR/issue-54505-no-std.rs:43:16
40+
--> $DIR/issue-54505-no-std.rs:45:16
4141
|
4242
LL | take_range(0..=1);
4343
| ^^^^^
@@ -49,7 +49,7 @@ LL | take_range(0..=1);
4949
found struct `core::ops::RangeInclusive<{integer}>`
5050

5151
error[E0308]: mismatched types
52-
--> $DIR/issue-54505-no-std.rs:48:16
52+
--> $DIR/issue-54505-no-std.rs:50:16
5353
|
5454
LL | take_range(..5);
5555
| ^^^
@@ -61,7 +61,7 @@ LL | take_range(..5);
6161
found struct `core::ops::RangeTo<{integer}>`
6262

6363
error[E0308]: mismatched types
64-
--> $DIR/issue-54505-no-std.rs:53:16
64+
--> $DIR/issue-54505-no-std.rs:55:16
6565
|
6666
LL | take_range(..=42);
6767
| ^^^^^

‎src/tools/tidy/src/pal.rs

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ const EXCEPTION_PATHS: &[&str] = &[
5959
"src/libstd/sys_common/mod.rs",
6060
"src/libstd/sys_common/net.rs",
6161
"src/libstd/sys_common/backtrace.rs",
62+
// panic_unwind shims
63+
"src/libstd/panicking.rs",
6264
"src/libterm", // Not sure how to make this crate portable, but test crate needs it.
6365
"src/libtest", // Probably should defer to unstable `std::sys` APIs.
6466
"src/libstd/sync/mpsc", // some tests are only run on non-emscripten

0 commit comments

Comments
 (0)
Please sign in to comment.