Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
58c6636
initial setup
kentosugama Mar 10, 2023
551bc8c
add wasm-opt crate
kentosugama Apr 6, 2023
9e77b4b
wasm-opt scaffolding
kentosugama Apr 6, 2023
cbc47eb
remove boiler
kentosugama Apr 6, 2023
959a69d
scaffold
kentosugama Apr 6, 2023
cf8e985
extract the custom sectionsg
kentosugama Apr 6, 2023
645fc35
reinsert data sections
kentosugama Apr 6, 2023
6c41502
let user specify optimization level
kentosugama Apr 6, 2023
077559f
use proper temp file
kentosugama Apr 7, 2023
a7af326
recurse for actor classes
kentosugama Apr 7, 2023
c59205e
nicer error messages
kentosugama Apr 7, 2023
ccd1622
add some file tests
kentosugama Apr 7, 2023
65f18ca
add semantic tests
kentosugama Apr 7, 2023
2fae562
test different optimization levels
kentosugama Apr 10, 2023
a8e62ac
make test cases more complex
kentosugama Apr 10, 2023
a07a95a
test preservation of metadata sections
kentosugama Apr 10, 2023
9df6cbc
Add documentation in readme
kentosugama Apr 10, 2023
21dced7
Update README.md
kentosugama Apr 10, 2023
09d0424
move into shrink and remove test cases
kentosugama Apr 10, 2023
ec44977
merge conflict
kentosugama Apr 10, 2023
b40649c
move tests into shrink test
kentosugama Apr 10, 2023
d9229cd
change error handling to not exit
kentosugama Apr 10, 2023
deec640
remove a reparsing of module
kentosugama Apr 11, 2023
82fcb3d
update tests
kentosugama Apr 11, 2023
da85724
Bump version
kentosugama Apr 11, 2023
c163dcf
inline call to list_metadata
kentosugama Apr 11, 2023
1427f59
remove keep_name_section and rename function
kentosugama Apr 11, 2023
dd0b87c
remove redundant get
kentosugama Apr 11, 2023
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
Prev Previous commit
Next Next commit
remove boiler
  • Loading branch information
kentosugama committed Apr 6, 2023
commit cbc47eb8263d4ef5d59597f4ad10574e92b1af7b
213 changes: 0 additions & 213 deletions src/optimize.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,8 @@
use crate::utils::*;
use std::path::PathBuf;
use walrus::ir::*;
use walrus::*;
use wasm_opt::OptimizationOptions;

#[derive(Copy, Clone)]
struct CallNew {
old_call_new: FunctionId,
new_call_new: FunctionId,
}

struct Replacer {
call_new: Option<CallNew>,
}
impl VisitorMut for Replacer {
fn visit_instr_mut(&mut self, instr: &mut Instr, _: &mut InstrLocId) {
if let Instr::Call(walrus::ir::Call { func }) = instr {
if let Some(ids) = &self.call_new {
if *func == ids.old_call_new {
*instr = Call {
func: ids.new_call_new,
}
.into();
}
}
}
}
}

pub fn optimize(m: &mut Module, keep_name_section: bool) {
let temp_file_name = "temp.opt.wasm";
let temp_path = PathBuf::from(temp_file_name);
Expand All @@ -43,191 +18,3 @@ pub fn optimize(m: &mut Module, keep_name_section: bool) {
// read back in from fs and assign back to m
*m = parse_wasm_file(temp_path, keep_name_section).unwrap();
}

fn make_redirect_call_new(m: &mut Module, redirect_id: &[u8]) -> FunctionId {
// Specify the same args as `call_new` so that WASM will correctly check mismatching args
let callee_src = m.locals.add(ValType::I32);
let callee_size = m.locals.add(ValType::I32);
let name_src = m.locals.add(ValType::I32);
let name_size = m.locals.add(ValType::I32);
let arg5 = m.locals.add(ValType::I32);
let arg6 = m.locals.add(ValType::I32);
let arg7 = m.locals.add(ValType::I32);
let arg8 = m.locals.add(ValType::I32);
let call_new = get_ic_func_id(m, "call_new");

let memory = get_memory_id(m);

// Scratch variables
let no_redirect = m.locals.add(ValType::I32);
let mut memory_backup = Vec::new();
for _ in 0..redirect_id.len() {
memory_backup.push(m.locals.add(ValType::I32));
}

// All management canister functions that require controller permissions
// The following wasm code assumes that this list is non-empty
let controller_function_names = [
"create_canister",
"update_settings",
"install_code",
"uninstall_code",
"canister_status",
"stop_canister",
"start_canister",
"delete_canister",
];

let mut builder = FunctionBuilder::new(
&mut m.types,
&[
ValType::I32,
ValType::I32,
ValType::I32,
ValType::I32,
ValType::I32,
ValType::I32,
ValType::I32,
ValType::I32,
],
&[],
);

builder
.func_body()
.block(None, |checks| {
let checks_id = checks.id();

// Check that callee address is empty
checks
.local_get(callee_size)
.i32_const(0)
.binop(BinaryOp::I32Ne)
.local_tee(no_redirect)
.br_if(checks_id);

// Check if the function name is any of the ones to be redirected
for func_name in controller_function_names {
checks.block(None, |name_check| {
let name_check_id = name_check.id();
name_check
// Check that name_size is the same length as the function name
.local_get(name_size)
.i32_const(func_name.len() as i32)
.binop(BinaryOp::I32Ne)
.br_if(name_check_id);

// Load the string at name_src onto the stack and compare it to the function name
for i in 0..func_name.len() {
name_check.local_get(name_src).load(
memory,
LoadKind::I32_8 {
kind: ExtendedLoad::SignExtend,
},
MemArg {
offset: i as u32,
align: 1,
},
);
}
for c in func_name.chars().rev() {
name_check
.i32_const(c as i32)
.binop(BinaryOp::I32Ne)
.br_if(name_check_id);
}
// Function names were equal, so skip all remaining checks and redirect
name_check.i32_const(0).local_set(no_redirect).br(checks_id);
});
}

// None of the function names matched
checks.i32_const(1).local_set(no_redirect);
})
.local_get(no_redirect)
.if_else(
None,
|block| {
// Put all the args back on stack and call call_new without redirecting
block
.local_get(callee_src)
.local_get(callee_size)
.local_get(name_src)
.local_get(name_size)
.local_get(arg5)
.local_get(arg6)
.local_get(arg7)
.local_get(arg8)
.call(call_new);
},
|block| {
// Save current memory starting from address 0 into local variables
for (address, backup_var) in memory_backup.iter().enumerate() {
block
.i32_const(address as i32)
.load(
memory,
LoadKind::I32_8 {
kind: ExtendedLoad::SignExtend,
},
MemArg {
offset: 0,
align: 1,
},
)
.local_set(*backup_var);
}

// Write the canister id into memory at address 0
for (address, byte) in redirect_id.iter().enumerate() {
block
.i32_const(address as i32)
.i32_const(*byte as i32)
.store(
memory,
StoreKind::I32_8 { atomic: false },
MemArg {
offset: 0,
align: 1,
},
);
}

block
.i32_const(0)
.i32_const(redirect_id.len() as i32)
.local_get(name_src)
.local_get(name_size)
.local_get(arg5)
.local_get(arg6)
.local_get(arg7)
.local_get(arg8)
.call(call_new);

// Restore old memory
for (address, byte) in memory_backup.iter().enumerate() {
block.i32_const(address as i32).local_get(*byte).store(
memory,
StoreKind::I32_8 { atomic: false },
MemArg {
offset: 0,
align: 1,
},
);
}
},
);
builder.finish(
vec![
callee_src,
callee_size,
name_src,
name_size,
arg5,
arg6,
arg7,
arg8,
],
&mut m.funcs,
)
}