mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-04-22 09:07:59 +00:00
cb023973e8
* Remove unnecessary references * Shorten expressions * Remove unnecessary reference * Simplify expression * Fix formatting
346 lines
12 KiB
Rust
346 lines
12 KiB
Rust
#[cfg(not(features = "std"))]
|
|
use crate::std::collections::BTreeMap as Map;
|
|
#[cfg(features = "std")]
|
|
use crate::std::collections::HashMap as Map;
|
|
|
|
use crate::std::{num::NonZeroU32, str::FromStr};
|
|
use parity_wasm::elements::Instruction;
|
|
|
|
pub struct UnknownInstruction;
|
|
|
|
/// An interface that describes instruction costs.
|
|
pub trait Rules {
|
|
/// Returns the cost for the passed `instruction`.
|
|
///
|
|
/// Returning `None` makes the gas instrumention end with an error. This is meant
|
|
/// as a way to have a partial rule set where any instruction that is not specifed
|
|
/// is considered as forbidden.
|
|
fn instruction_cost(&self, instruction: &Instruction) -> Option<u32>;
|
|
|
|
/// Returns the costs for growing the memory using the `memory.grow` instruction.
|
|
///
|
|
/// Please note that these costs are in addition to the costs specified by `instruction_cost`
|
|
/// for the `memory.grow` instruction. Specifying `None` leads to no additional charge.
|
|
/// Those are meant as dynamic costs which take the amount of pages that the memory is
|
|
/// grown by into consideration. This is not possible using `instruction_cost` because
|
|
/// those costs depend on the stack and must be injected as code into the function calling
|
|
/// `memory.grow`. Therefore returning `Some` comes with a performance cost.
|
|
fn memory_grow_cost(&self) -> Option<MemoryGrowCost>;
|
|
}
|
|
|
|
/// Dynamic costs for memory growth.
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
pub enum MemoryGrowCost {
|
|
/// Charge the specified amount for each page that the memory is grown by.
|
|
Linear(NonZeroU32),
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
pub enum Metering {
|
|
Regular,
|
|
Forbidden,
|
|
Fixed(u32),
|
|
}
|
|
|
|
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
|
pub enum InstructionType {
|
|
Bit,
|
|
Add,
|
|
Mul,
|
|
Div,
|
|
Load,
|
|
Store,
|
|
Const,
|
|
FloatConst,
|
|
Local,
|
|
Global,
|
|
ControlFlow,
|
|
IntegerComparison,
|
|
FloatComparison,
|
|
Float,
|
|
Conversion,
|
|
FloatConversion,
|
|
Reinterpretation,
|
|
Unreachable,
|
|
Nop,
|
|
CurrentMemory,
|
|
GrowMemory,
|
|
}
|
|
|
|
impl FromStr for InstructionType {
|
|
type Err = UnknownInstruction;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"bit" => Ok(InstructionType::Bit),
|
|
"add" => Ok(InstructionType::Add),
|
|
"mul" => Ok(InstructionType::Mul),
|
|
"div" => Ok(InstructionType::Div),
|
|
"load" => Ok(InstructionType::Load),
|
|
"store" => Ok(InstructionType::Store),
|
|
"const" => Ok(InstructionType::Const),
|
|
"local" => Ok(InstructionType::Local),
|
|
"global" => Ok(InstructionType::Global),
|
|
"flow" => Ok(InstructionType::ControlFlow),
|
|
"integer_comp" => Ok(InstructionType::IntegerComparison),
|
|
"float_comp" => Ok(InstructionType::FloatComparison),
|
|
"float" => Ok(InstructionType::Float),
|
|
"conversion" => Ok(InstructionType::Conversion),
|
|
"float_conversion" => Ok(InstructionType::FloatConversion),
|
|
"reinterpret" => Ok(InstructionType::Reinterpretation),
|
|
"unreachable" => Ok(InstructionType::Unreachable),
|
|
"nop" => Ok(InstructionType::Nop),
|
|
"current_mem" => Ok(InstructionType::CurrentMemory),
|
|
"grow_mem" => Ok(InstructionType::GrowMemory),
|
|
_ => Err(UnknownInstruction),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl InstructionType {
|
|
pub fn op(instruction: &Instruction) -> Self {
|
|
use Instruction::*;
|
|
|
|
match *instruction {
|
|
Unreachable => InstructionType::Unreachable,
|
|
Nop => InstructionType::Nop,
|
|
Block(_) => InstructionType::ControlFlow,
|
|
Loop(_) => InstructionType::ControlFlow,
|
|
If(_) => InstructionType::ControlFlow,
|
|
Else => InstructionType::ControlFlow,
|
|
End => InstructionType::ControlFlow,
|
|
Br(_) => InstructionType::ControlFlow,
|
|
BrIf(_) => InstructionType::ControlFlow,
|
|
BrTable(_) => InstructionType::ControlFlow,
|
|
Return => InstructionType::ControlFlow,
|
|
Call(_) => InstructionType::ControlFlow,
|
|
CallIndirect(_, _) => InstructionType::ControlFlow,
|
|
Drop => InstructionType::ControlFlow,
|
|
Select => InstructionType::ControlFlow,
|
|
|
|
GetLocal(_) => InstructionType::Local,
|
|
SetLocal(_) => InstructionType::Local,
|
|
TeeLocal(_) => InstructionType::Local,
|
|
GetGlobal(_) => InstructionType::Global,
|
|
SetGlobal(_) => InstructionType::Global,
|
|
|
|
I32Load(_, _) => InstructionType::Load,
|
|
I64Load(_, _) => InstructionType::Load,
|
|
F32Load(_, _) => InstructionType::Load,
|
|
F64Load(_, _) => InstructionType::Load,
|
|
I32Load8S(_, _) => InstructionType::Load,
|
|
I32Load8U(_, _) => InstructionType::Load,
|
|
I32Load16S(_, _) => InstructionType::Load,
|
|
I32Load16U(_, _) => InstructionType::Load,
|
|
I64Load8S(_, _) => InstructionType::Load,
|
|
I64Load8U(_, _) => InstructionType::Load,
|
|
I64Load16S(_, _) => InstructionType::Load,
|
|
I64Load16U(_, _) => InstructionType::Load,
|
|
I64Load32S(_, _) => InstructionType::Load,
|
|
I64Load32U(_, _) => InstructionType::Load,
|
|
|
|
I32Store(_, _) => InstructionType::Store,
|
|
I64Store(_, _) => InstructionType::Store,
|
|
F32Store(_, _) => InstructionType::Store,
|
|
F64Store(_, _) => InstructionType::Store,
|
|
I32Store8(_, _) => InstructionType::Store,
|
|
I32Store16(_, _) => InstructionType::Store,
|
|
I64Store8(_, _) => InstructionType::Store,
|
|
I64Store16(_, _) => InstructionType::Store,
|
|
I64Store32(_, _) => InstructionType::Store,
|
|
|
|
CurrentMemory(_) => InstructionType::CurrentMemory,
|
|
GrowMemory(_) => InstructionType::GrowMemory,
|
|
|
|
I32Const(_) => InstructionType::Const,
|
|
I64Const(_) => InstructionType::Const,
|
|
|
|
F32Const(_) => InstructionType::FloatConst,
|
|
F64Const(_) => InstructionType::FloatConst,
|
|
|
|
I32Eqz => InstructionType::IntegerComparison,
|
|
I32Eq => InstructionType::IntegerComparison,
|
|
I32Ne => InstructionType::IntegerComparison,
|
|
I32LtS => InstructionType::IntegerComparison,
|
|
I32LtU => InstructionType::IntegerComparison,
|
|
I32GtS => InstructionType::IntegerComparison,
|
|
I32GtU => InstructionType::IntegerComparison,
|
|
I32LeS => InstructionType::IntegerComparison,
|
|
I32LeU => InstructionType::IntegerComparison,
|
|
I32GeS => InstructionType::IntegerComparison,
|
|
I32GeU => InstructionType::IntegerComparison,
|
|
|
|
I64Eqz => InstructionType::IntegerComparison,
|
|
I64Eq => InstructionType::IntegerComparison,
|
|
I64Ne => InstructionType::IntegerComparison,
|
|
I64LtS => InstructionType::IntegerComparison,
|
|
I64LtU => InstructionType::IntegerComparison,
|
|
I64GtS => InstructionType::IntegerComparison,
|
|
I64GtU => InstructionType::IntegerComparison,
|
|
I64LeS => InstructionType::IntegerComparison,
|
|
I64LeU => InstructionType::IntegerComparison,
|
|
I64GeS => InstructionType::IntegerComparison,
|
|
I64GeU => InstructionType::IntegerComparison,
|
|
|
|
F32Eq => InstructionType::FloatComparison,
|
|
F32Ne => InstructionType::FloatComparison,
|
|
F32Lt => InstructionType::FloatComparison,
|
|
F32Gt => InstructionType::FloatComparison,
|
|
F32Le => InstructionType::FloatComparison,
|
|
F32Ge => InstructionType::FloatComparison,
|
|
|
|
F64Eq => InstructionType::FloatComparison,
|
|
F64Ne => InstructionType::FloatComparison,
|
|
F64Lt => InstructionType::FloatComparison,
|
|
F64Gt => InstructionType::FloatComparison,
|
|
F64Le => InstructionType::FloatComparison,
|
|
F64Ge => InstructionType::FloatComparison,
|
|
|
|
I32Clz => InstructionType::Bit,
|
|
I32Ctz => InstructionType::Bit,
|
|
I32Popcnt => InstructionType::Bit,
|
|
I32Add => InstructionType::Add,
|
|
I32Sub => InstructionType::Add,
|
|
I32Mul => InstructionType::Mul,
|
|
I32DivS => InstructionType::Div,
|
|
I32DivU => InstructionType::Div,
|
|
I32RemS => InstructionType::Div,
|
|
I32RemU => InstructionType::Div,
|
|
I32And => InstructionType::Bit,
|
|
I32Or => InstructionType::Bit,
|
|
I32Xor => InstructionType::Bit,
|
|
I32Shl => InstructionType::Bit,
|
|
I32ShrS => InstructionType::Bit,
|
|
I32ShrU => InstructionType::Bit,
|
|
I32Rotl => InstructionType::Bit,
|
|
I32Rotr => InstructionType::Bit,
|
|
|
|
I64Clz => InstructionType::Bit,
|
|
I64Ctz => InstructionType::Bit,
|
|
I64Popcnt => InstructionType::Bit,
|
|
I64Add => InstructionType::Add,
|
|
I64Sub => InstructionType::Add,
|
|
I64Mul => InstructionType::Mul,
|
|
I64DivS => InstructionType::Div,
|
|
I64DivU => InstructionType::Div,
|
|
I64RemS => InstructionType::Div,
|
|
I64RemU => InstructionType::Div,
|
|
I64And => InstructionType::Bit,
|
|
I64Or => InstructionType::Bit,
|
|
I64Xor => InstructionType::Bit,
|
|
I64Shl => InstructionType::Bit,
|
|
I64ShrS => InstructionType::Bit,
|
|
I64ShrU => InstructionType::Bit,
|
|
I64Rotl => InstructionType::Bit,
|
|
I64Rotr => InstructionType::Bit,
|
|
|
|
F32Abs => InstructionType::Float,
|
|
F32Neg => InstructionType::Float,
|
|
F32Ceil => InstructionType::Float,
|
|
F32Floor => InstructionType::Float,
|
|
F32Trunc => InstructionType::Float,
|
|
F32Nearest => InstructionType::Float,
|
|
F32Sqrt => InstructionType::Float,
|
|
F32Add => InstructionType::Float,
|
|
F32Sub => InstructionType::Float,
|
|
F32Mul => InstructionType::Float,
|
|
F32Div => InstructionType::Float,
|
|
F32Min => InstructionType::Float,
|
|
F32Max => InstructionType::Float,
|
|
F32Copysign => InstructionType::Float,
|
|
F64Abs => InstructionType::Float,
|
|
F64Neg => InstructionType::Float,
|
|
F64Ceil => InstructionType::Float,
|
|
F64Floor => InstructionType::Float,
|
|
F64Trunc => InstructionType::Float,
|
|
F64Nearest => InstructionType::Float,
|
|
F64Sqrt => InstructionType::Float,
|
|
F64Add => InstructionType::Float,
|
|
F64Sub => InstructionType::Float,
|
|
F64Mul => InstructionType::Float,
|
|
F64Div => InstructionType::Float,
|
|
F64Min => InstructionType::Float,
|
|
F64Max => InstructionType::Float,
|
|
F64Copysign => InstructionType::Float,
|
|
|
|
I32WrapI64 => InstructionType::Conversion,
|
|
I64ExtendSI32 => InstructionType::Conversion,
|
|
I64ExtendUI32 => InstructionType::Conversion,
|
|
|
|
I32TruncSF32 => InstructionType::FloatConversion,
|
|
I32TruncUF32 => InstructionType::FloatConversion,
|
|
I32TruncSF64 => InstructionType::FloatConversion,
|
|
I32TruncUF64 => InstructionType::FloatConversion,
|
|
I64TruncSF32 => InstructionType::FloatConversion,
|
|
I64TruncUF32 => InstructionType::FloatConversion,
|
|
I64TruncSF64 => InstructionType::FloatConversion,
|
|
I64TruncUF64 => InstructionType::FloatConversion,
|
|
F32ConvertSI32 => InstructionType::FloatConversion,
|
|
F32ConvertUI32 => InstructionType::FloatConversion,
|
|
F32ConvertSI64 => InstructionType::FloatConversion,
|
|
F32ConvertUI64 => InstructionType::FloatConversion,
|
|
F32DemoteF64 => InstructionType::FloatConversion,
|
|
F64ConvertSI32 => InstructionType::FloatConversion,
|
|
F64ConvertUI32 => InstructionType::FloatConversion,
|
|
F64ConvertSI64 => InstructionType::FloatConversion,
|
|
F64ConvertUI64 => InstructionType::FloatConversion,
|
|
F64PromoteF32 => InstructionType::FloatConversion,
|
|
|
|
I32ReinterpretF32 => InstructionType::Reinterpretation,
|
|
I64ReinterpretF64 => InstructionType::Reinterpretation,
|
|
F32ReinterpretI32 => InstructionType::Reinterpretation,
|
|
F64ReinterpretI64 => InstructionType::Reinterpretation,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Set {
|
|
regular: u32,
|
|
entries: Map<InstructionType, Metering>,
|
|
grow: u32,
|
|
}
|
|
|
|
impl Default for Set {
|
|
fn default() -> Self {
|
|
Set { regular: 1, entries: Map::new(), grow: 0 }
|
|
}
|
|
}
|
|
|
|
impl Set {
|
|
pub fn new(regular: u32, entries: Map<InstructionType, Metering>) -> Self {
|
|
Set { regular, entries, grow: 0 }
|
|
}
|
|
|
|
pub fn grow_cost(&self) -> u32 {
|
|
self.grow
|
|
}
|
|
|
|
pub fn with_grow_cost(mut self, val: u32) -> Self {
|
|
self.grow = val;
|
|
self
|
|
}
|
|
|
|
pub fn with_forbidden_floats(mut self) -> Self {
|
|
self.entries.insert(InstructionType::Float, Metering::Forbidden);
|
|
self.entries.insert(InstructionType::FloatComparison, Metering::Forbidden);
|
|
self.entries.insert(InstructionType::FloatConst, Metering::Forbidden);
|
|
self.entries.insert(InstructionType::FloatConversion, Metering::Forbidden);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Rules for Set {
|
|
fn instruction_cost(&self, instruction: &Instruction) -> Option<u32> {
|
|
match self.entries.get(&InstructionType::op(instruction)) {
|
|
None | Some(Metering::Regular) => Some(self.regular),
|
|
Some(Metering::Fixed(val)) => Some(*val),
|
|
Some(Metering::Forbidden) => None,
|
|
}
|
|
}
|
|
|
|
fn memory_grow_cost(&self) -> Option<MemoryGrowCost> {
|
|
NonZeroU32::new(self.grow).map(MemoryGrowCost::Linear)
|
|
}
|
|
}
|