Skip to content

Commit 34ce3d8

Browse files
committed
Implement timeout support in the runtime, update dependencies
1 parent 34c55f8 commit 34ce3d8

File tree

10 files changed

+323
-49
lines changed

10 files changed

+323
-49
lines changed

Cargo.lock

Lines changed: 184 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ authors = ["Gregor Peach <[email protected]>"]
55
edition = "2018"
66

77
[dependencies]
8+
flexi_logger = "0.14.4"
89
llvm-alt = { git = "https://github.com/Others/llvm-rs.git"}
9-
structopt = "0.2"
10-
wasmparser = "0.29"
10+
log = "0.4.8"
11+
structopt = "0.3.2"
12+
wasmparser = "0.39.2"
1113

1214
[profile.release]
1315
debug = true

code_benches/memcmp/memcmp.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
volatile char a[] = "123456";
2+
volatile char b[] = "123YF";
3+
4+
5+
__attribute__ ((used))
6+
int my_memcmp(volatile char *vl, volatile char *vr, unsigned long n)
7+
{
8+
volatile char *l=vl, *r=vr;
9+
for (; n && *l == *r; n--, l++, r++);
10+
return n ? *l-*r : 0;
11+
}
12+
13+
int main() {
14+
int v = my_memcmp(a, b, 4);
15+
if (v != 0) {
16+
return 0;
17+
} else {
18+
return -1;
19+
}
20+
}

code_benches/run.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def __str__(self):
5454
# TODO: Fix sphinx, which doesn't work properly on OS X
5555
# TODO: Do ghostscript, which has a ton of files
5656
programs = [
57+
Program("memcmp", [], 2 ** 14),
58+
5759
# Real world program benchmarks
5860
Program("libjpeg", [], 2 ** 15,
5961
custom_arguments=["-Wno-incompatible-library-redeclaration", "-Wno-implicit-function-declaration", "-Wno-shift-negative-value"]),

runtime/runtime.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,27 @@ INLINE double f64_max(double a, double b) {
166166
}
167167

168168

169+
// this provides a way to add a timeout for wasm execution, and support for that
170+
// TODO: Add a way for silverfish to give us this value
171+
WEAK unsigned int wasm_execution_timeout_ms = 0;
172+
sigjmp_buf timeout_jump;
173+
#define JUMPING_BACK 0xBACC
174+
175+
// Precondition: you've already loaded timeout_jump with where we should jump after the timeout
176+
void handle_sigalarm(int sig) {
177+
// TODO: We use siglongjmp which resets signal stuff, so perhaps this call is unnessesary
178+
signal(sig, SIG_IGN);
179+
siglongjmp(timeout_jump, JUMPING_BACK);
180+
}
181+
182+
void schedule_timeout() {
183+
signal(SIGALRM, handle_sigalarm);
184+
ualarm(wasm_execution_timeout_ms * 1000, 0);
185+
}
186+
187+
void cancel_timeout() {
188+
ualarm(0, 0);
189+
}
169190

170191
// If we are using runtime globals, we need to populate them
171192
WEAK void populate_globals() {}
@@ -178,11 +199,25 @@ int main(int argc, char* argv[]) {
178199
alloc_linear_memory();
179200
populate_table();
180201

202+
// Setup the global values (if needed), and populate the linear memory
181203
switch_out_of_runtime();
182204
populate_globals();
183205
switch_into_runtime();
184206
populate_memory();
185207

208+
// In the case of a real timeout being compiled in, handle that
209+
if (wasm_execution_timeout_ms) {
210+
// Set the jumpoint to here, and save the signal mask
211+
int res = sigsetjmp(timeout_jump, 1);
212+
if (res != 0) {
213+
assert(res == JUMPING_BACK);
214+
printf("WE DECIDED TO GIVE UP\n");
215+
return -JUMPING_BACK;
216+
}
217+
schedule_timeout();
218+
}
219+
220+
186221
// What follows is a huge cludge
187222
// We want to pass the program arguments to the program, so we need to copy them into an array in linear memory
188223
// This is about as nice as you would expect...
@@ -213,5 +248,10 @@ int main(int argc, char* argv[]) {
213248
int ret = wasmf_main(argc, page_offset);
214249
switch_into_runtime();
215250

251+
// Cancel any pending timeout
252+
if (wasm_execution_timeout_ms) {
253+
cancel_timeout();
254+
}
255+
216256
return ret;
217257
}

runtime/runtime.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <limits.h>
55
#include <math.h>
66
#include <printf.h>
7+
#include <setjmp.h>
8+
#include <signal.h>
79
#include <stdlib.h>
810
#include <stdint.h>
911
#include <stdio.h>
@@ -52,7 +54,6 @@ extern u32 memory_size;
5254

5355
void alloc_linear_memory();
5456
void expand_memory();
55-
// Assumption: bounds_check < WASM_PAGE_SIZE
5657
INLINE char* get_memory_ptr_for_runtime(u32 offset, u32 bounds_check);
5758

5859
static inline void* get_memory_ptr_void(u32 offset, u32 bounds_check) {
@@ -87,4 +88,3 @@ INLINE char* get_function_from_table(u32 idx, u32 type_id);
8788

8889
// libc/* might need to do some setup for the libc setup
8990
void stub_init(i32 offset);
90-

src/codegen/breakout.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ use llvm::Builder;
66
use llvm::Context;
77
use llvm::Value;
88

9-
use wasmparser::Type;
9+
use wasmparser::TypeOrFuncType;
1010

1111
use super::type_conversions::llvm_type_to_zeroed_value;
1212
use super::type_conversions::wasm_type_to_zeroed_value;
1313

1414
pub struct BreakoutTarget<'a> {
1515
pub bb: &'a BasicBlock,
16-
result_type: Option<Type>,
16+
result_type: Option<TypeOrFuncType>,
1717
jumps: Vec<JumpFrom<'a>>,
1818
}
1919

@@ -26,15 +26,15 @@ pub struct JumpFrom<'a> {
2626
pub type WBreakoutTarget<'a> = Rc<RefCell<BreakoutTarget<'a>>>;
2727

2828
impl<'a> BreakoutTarget<'a> {
29-
pub fn new(bb: &'a BasicBlock, result_type: Option<Type>) -> BreakoutTarget<'a> {
29+
pub fn new(bb: &'a BasicBlock, result_type: Option<TypeOrFuncType>) -> BreakoutTarget<'a> {
3030
BreakoutTarget {
3131
bb,
3232
result_type,
3333
jumps: Vec::new(),
3434
}
3535
}
3636

37-
pub fn new_wrapped(bb: &'a BasicBlock, result_type: Option<Type>) -> WBreakoutTarget<'a> {
37+
pub fn new_wrapped(bb: &'a BasicBlock, result_type: Option<TypeOrFuncType>) -> WBreakoutTarget<'a> {
3838
let bt = Self::new(bb, result_type);
3939
Rc::new(RefCell::new(bt))
4040
}
@@ -112,7 +112,11 @@ impl<'a> BreakoutTarget<'a> {
112112
if vals.is_empty() {
113113
// See above comment about unreachablity
114114
// TODO: Figure out if we can instead produce an unreachable result
115-
wasm_type_to_zeroed_value(ctx, ty)
115+
if let TypeOrFuncType::Type(t) = ty {
116+
wasm_type_to_zeroed_value(ctx, t)
117+
} else {
118+
panic!()
119+
}
116120
} else if vals.len() == 1 {
117121
vals[0].1
118122
} else {

src/codegen/function.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use super::block::compile_block;
1111
use super::breakout::BreakoutTarget;
1212
use super::type_conversions::wasm_type_to_zeroed_value;
1313
use super::ModuleCtx;
14+
use wasmparser::TypeOrFuncType;
1415

1516
pub struct FunctionCtx<'a> {
1617
pub llvm_f: &'a Function,
@@ -53,7 +54,7 @@ pub fn compile_function(ctx: &ModuleCtx, f: &ImplementedFunction) {
5354
};
5455

5556
let termination_block = llvm_f.append("exit");
56-
let root_breakout_target = BreakoutTarget::new_wrapped(termination_block, f.get_return_type());
57+
let root_breakout_target = BreakoutTarget::new_wrapped(termination_block, f.get_return_type().map(TypeOrFuncType::Type));
5758

5859
// In WASM, a break out of the root block is the same as returning from the function
5960
// Thus the termination block needs to do that

src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
extern crate llvm;
2+
#[macro_use]
3+
extern crate log;
24
extern crate structopt;
35
extern crate wasmparser;
46

@@ -43,7 +45,11 @@ pub struct Opt {
4345
}
4446

4547
fn main() -> io::Result<()> {
48+
let log_spec = "debug";
49+
flexi_logger::Logger::with_str(log_spec).start().unwrap();
50+
4651
let opt = Opt::from_args();
52+
info!("running silverfish({:?}, {:?})", opt.input, opt.output);
4753

4854
let input_path: &Path = &opt.input;
4955
let input_filename = input_path.file_name().and_then(|s| s.to_str()).unwrap();
@@ -56,11 +62,15 @@ fn main() -> io::Result<()> {
5662
let mut parser = Parser::new(&wasm_bytes);
5763
let module = WasmModule::from_wasm_parser(input_filename, &mut parser);
5864

65+
// Get diagnostics
66+
module.log_diagnostics();
67+
5968
let output_path = opt
6069
.output
6170
.clone()
6271
.unwrap_or_else(|| "output.bc".to_string());
6372
process_to_llvm(&opt, module, &output_path)?;
6473

74+
info!("silverfish finished successfully");
6575
Ok(())
6676
}

src/wasm.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::str;
22

3-
use wasmparser::CustomSectionKind;
3+
use wasmparser::{CustomSectionKind, TypeOrFuncType};
44
use wasmparser::ExternalKind;
55
use wasmparser::FuncType;
66
use wasmparser::ImportSectionEntryType;
@@ -63,6 +63,32 @@ impl Global {
6363
},
6464
}
6565
}
66+
67+
pub fn in_memory_size(&self) -> usize {
68+
let typ = match self {
69+
Global::Imported {
70+
content_type,
71+
..
72+
} => content_type,
73+
Global::InModule {
74+
content_type,
75+
..
76+
} => content_type
77+
};
78+
79+
match typ {
80+
Type::I32 => 4,
81+
Type::I64 => 8,
82+
Type::F32 => 4,
83+
Type::F64 => 8,
84+
Type::V128 => 16,
85+
Type::AnyFunc => 4,
86+
Type::AnyRef => 8,
87+
Type::Func => 4,
88+
Type::EmptyBlockType => 0,
89+
Type::Null => 0,
90+
}
91+
}
6692
}
6793

6894
#[derive(Clone, Debug)]
@@ -217,8 +243,8 @@ pub struct TableInitializer {
217243

218244
#[derive(Clone, Debug, PartialEq)]
219245
pub enum Instruction {
220-
BlockStart { produced_type: Option<Type> },
221-
LoopStart { produced_type: Option<Type> },
246+
BlockStart { produced_type: Option<TypeOrFuncType> },
247+
LoopStart { produced_type: Option<TypeOrFuncType> },
222248
End,
223249

224250
Br { depth: u32 },
@@ -418,15 +444,15 @@ impl<'a> From<&'a Operator<'a>> for Instruction {
418444
fn from(o: &Operator<'a>) -> Self {
419445
match *o {
420446
Operator::Block { ty } => {
421-
let produced_type = if ty == Type::EmptyBlockType {
447+
let produced_type = if ty == TypeOrFuncType::Type(Type::EmptyBlockType) {
422448
None
423449
} else {
424450
Some(ty)
425451
};
426452
Instruction::BlockStart { produced_type }
427453
}
428454
Operator::Loop { ty } => {
429-
let produced_type = if ty == Type::EmptyBlockType {
455+
let produced_type = if ty == TypeOrFuncType::Type(Type::EmptyBlockType) {
430456
None
431457
} else {
432458
Some(ty)
@@ -801,6 +827,25 @@ impl WasmModule {
801827
m
802828
}
803829

830+
pub fn log_diagnostics(&self) {
831+
let global_variable_memory_use: usize = self.globals.iter().map(|g| g.in_memory_size()).sum();
832+
info!("Globals taking up {} bytes", global_variable_memory_use);
833+
834+
let mut data_initializer_memory_use = 0;
835+
for initializer in &self.data_initializers {
836+
for body_bytes in &initializer.body {
837+
data_initializer_memory_use += body_bytes.len();
838+
}
839+
}
840+
info!("Data initializers taking up {} bytes", data_initializer_memory_use);
841+
842+
let mut function_table_entries = 0;
843+
for initializer in &self.table_initializers {
844+
function_table_entries += initializer.function_indexes.len();
845+
}
846+
info!("Function table entries {} (ignoring fragmentation)", function_table_entries);
847+
}
848+
804849
fn implement_function(&mut self, mut f: ImplementedFunction) {
805850
for existing_function in &mut self.functions {
806851
if let Some((ty, ty_index)) = existing_function.declared_but_unimplemented() {

0 commit comments

Comments
 (0)