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 Tests for native wasm exceptions #113247

Merged
merged 5 commits into from
Jul 9, 2023
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
6 changes: 4 additions & 2 deletions src/ci/docker/host-x86_64/test-various/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
qemu-system-x86 \
&& rm -rf /var/lib/apt/lists/*

RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \
RUN curl -sL https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-x64.tar.xz | \
tar -xJ

# Install 32-bit OVMF files for the i686-unknown-uefi test. This package
Expand All @@ -42,7 +42,7 @@ RUN sh /scripts/sccache.sh

ENV RUST_CONFIGURE_ARGS \
--musl-root-x86_64=/usr/local/x86_64-linux-musl \
--set build.nodejs=/node-v15.14.0-linux-x64/bin/node \
--set build.nodejs=/node-v18.12.0-linux-x64/bin/node \
--set rust.lld

# Some run-make tests have assertions about code size, and enabling debug
Expand All @@ -58,6 +58,8 @@ ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_T
tests/ui \
tests/mir-opt \
tests/codegen-units \
tests/codegen \
tests/assembly \
library/core

ENV NVPTX_TARGETS=nvptx64-nvidia-cuda
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// ignore-macos slightly different policy on stack protection of arrays
// ignore-windows stack check code uses different function names
// ignore-nvptx64 stack protector is not supported
// ignore-wasm32-bare
// [all] compile-flags: -Z stack-protector=all
// [strong] compile-flags: -Z stack-protector=strong
// [basic] compile-flags: -Z stack-protector=basic
Expand Down
60 changes: 60 additions & 0 deletions tests/assembly/wasm_exceptions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// only-wasm32-bare
// assembly-output: emit-asm
// compile-flags: -C target-feature=+exception-handling
// compile-flags: -C panic=unwind
// compile-flags: -C llvm-args=-wasm-enable-eh

#![crate_type = "lib"]
#![feature(core_intrinsics)]
#![feature(rustc_attrs)]

extern {
fn may_panic();

#[rustc_nounwind]
fn log_number(number: usize);
}

struct LogOnDrop;

impl Drop for LogOnDrop {
fn drop(&mut self) {
unsafe { log_number(0); }
}
}

// CHECK-LABEL: test_cleanup:
#[no_mangle]
pub fn test_cleanup() {
let _log_on_drop = LogOnDrop;
unsafe { may_panic(); }

// CHECK-NOT: call
// CHECK: try
// CHECK: call may_panic
// CHECK: catch_all
// CHECK: rethrow
// CHECK: end_try
}

// CHECK-LABEL: test_rtry:
#[no_mangle]
pub fn test_rtry() {
unsafe {
core::intrinsics::r#try(|_| {
may_panic();
}, core::ptr::null_mut(), |data, exception| {
log_number(data as usize);
log_number(exception as usize);
});
}

// CHECK-NOT: call
// CHECK: try
// CHECK: call may_panic
// CHECK: catch
// CHECK: call log_number
// CHECK: call log_number
// CHECK-NOT: rethrow
// CHECK: end_try
}
1 change: 1 addition & 0 deletions tests/codegen/repr-transparent-aggregates-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// ignore-s390x
// ignore-windows
// ignore-loongarch64
// ignore-wasm32-bare
// See repr-transparent.rs

#![feature(transparent_unions)]
Expand Down
51 changes: 51 additions & 0 deletions tests/codegen/wasm_exceptions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// only-wasm32-bare
// compile-flags: -C panic=unwind

#![crate_type = "lib"]
#![feature(core_intrinsics)]
#![feature(rustc_attrs)]

extern {
fn may_panic();

#[rustc_nounwind]
fn log_number(number: usize);
}

struct LogOnDrop;

impl Drop for LogOnDrop {
fn drop(&mut self) {
unsafe { log_number(0); }
}
}

// CHECK-LABEL: @test_cleanup() {{.*}} @__gxx_wasm_personality_v0
#[no_mangle]
pub fn test_cleanup() {
let _log_on_drop = LogOnDrop;
unsafe { may_panic(); }

// CHECK-NOT: call
// CHECK: invoke void @may_panic()
// CHECK: %cleanuppad = cleanuppad within none []
}

// CHECK-LABEL: @test_rtry() {{.*}} @__gxx_wasm_personality_v0
#[no_mangle]
pub fn test_rtry() {
unsafe {
core::intrinsics::r#try(|_| {
may_panic();
}, core::ptr::null_mut(), |data, exception| {
log_number(data as usize);
log_number(exception as usize);
});
}

// CHECK-NOT: call
// CHECK: invoke void @may_panic()
// CHECK: {{.*}} = catchswitch within none [label {{.*}}] unwind to caller
// CHECK: {{.*}} = catchpad within {{.*}} [ptr null]
// CHECK: catchret
}
12 changes: 12 additions & 0 deletions tests/run-make/wasm-exceptions-nostd/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
include ../tools.mk

# only-wasm32-bare

# Add a few command line args to make exceptions work
RUSTC := $(RUSTC) -C llvm-args=-wasm-enable-eh
RUSTC := $(RUSTC) -C target-feature=+exception-handling
RUSTC := $(RUSTC) -C panic=unwind

all:
$(RUSTC) src/lib.rs --target wasm32-unknown-unknown
$(NODE) verify.mjs $(TMPDIR)/lib.wasm
67 changes: 67 additions & 0 deletions tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell;

#[global_allocator]
static ALLOCATOR: ArenaAllocator = ArenaAllocator::new();

/// Very simple allocator which never deallocates memory
///
/// Based on the example from
/// https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html
pub struct ArenaAllocator {
arena: UnsafeCell<Arena>,
}

impl ArenaAllocator {
pub const fn new() -> Self {
Self {
arena: UnsafeCell::new(Arena::new()),
}
}
}

/// Safe because we are singlethreaded
unsafe impl Sync for ArenaAllocator {}

unsafe impl GlobalAlloc for ArenaAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let arena = &mut *self.arena.get();
arena.alloc(layout)
}

unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}

const ARENA_SIZE: usize = 64 * 1024; // more than enough

#[repr(C, align(4096))]
struct Arena {
buf: [u8; ARENA_SIZE], // aligned at 4096
allocated: usize,
}

impl Arena {
pub const fn new() -> Self {
Self {
buf: [0x55; ARENA_SIZE],
allocated: 0,
}
}

pub unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
if layout.align() > 4096 || layout.size() > ARENA_SIZE {
return core::ptr::null_mut();
}

let align_minus_one = layout.align() - 1;
let start = (self.allocated + align_minus_one) & !align_minus_one; // round up
let new_cursor = start + layout.size();

if new_cursor >= ARENA_SIZE {
return core::ptr::null_mut();
}

self.allocated = new_cursor;
self.buf.as_mut_ptr().add(start)
}
}
60 changes: 60 additions & 0 deletions tests/run-make/wasm-exceptions-nostd/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#![no_std]
#![crate_type = "cdylib"]

// Allow a few unstable features because we create a panic
// runtime for native wasm exceptions from scratch

#![feature(core_intrinsics)]
#![feature(lang_items)]
#![feature(link_llvm_intrinsics)]
#![feature(panic_info_message)]

extern crate alloc;

/// This module allows us to use `Box`, `String`, ... even in no-std
mod arena_alloc;

/// This module allows logging text, even in no-std
mod logging;

/// This module allows exceptions, even in no-std
#[cfg(target_arch = "wasm32")]
mod panicking;

use alloc::boxed::Box;
use alloc::string::String;

struct LogOnDrop;

impl Drop for LogOnDrop {
fn drop(&mut self) {
logging::log_str("Dropped");
}
}

#[allow(unreachable_code)]
#[allow(unconditional_panic)]
#[no_mangle]
pub extern "C" fn start() -> usize {
let data = 0x1234usize as *mut u8; // Something to recognize

unsafe {
core::intrinsics::r#try(|data: *mut u8| {
let _log_on_drop = LogOnDrop;

logging::log_str(&alloc::format!("`r#try` called with ptr {:?}", data));
let x = [12];
let _ = x[4]; // should panic

logging::log_str("This line should not be visible! :(");
}, data, |data, exception| {
let exception = *Box::from_raw(exception as *mut String);
logging::log_str("Caught something!");
logging::log_str(&alloc::format!(" data : {:?}", data));
logging::log_str(&alloc::format!(" exception: {:?}", exception));
});
}

logging::log_str("This program terminates correctly.");
0
}
9 changes: 9 additions & 0 deletions tests/run-make/wasm-exceptions-nostd/src/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extern "C" {
fn __log_utf8(ptr: *const u8, size: usize);
}

pub fn log_str(text: &str) {
unsafe {
__log_utf8(text.as_ptr(), text.len());
}
}
29 changes: 29 additions & 0 deletions tests/run-make/wasm-exceptions-nostd/src/panicking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#[lang = "eh_personality"]
fn eh_personality() {}

mod internal {
extern "C" {
#[link_name = "llvm.wasm.throw"]
pub fn wasm_throw(tag: i32, ptr: *mut u8) -> !;
}
}

unsafe fn wasm_throw(ptr: *mut u8) -> ! {
internal::wasm_throw(0, ptr);
}

#[panic_handler]
fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
use alloc::boxed::Box;
use alloc::string::ToString;

let msg = info
.message()
.map(|msg| msg.to_string())
.unwrap_or("(no message)".to_string());
let exception = Box::new(msg.to_string());
unsafe {
let exception_raw = Box::into_raw(exception);
wasm_throw(exception_raw as *mut u8);
}
}
Loading