pwasm_utils/
rules.rs

1#[cfg(not(features = "std"))]
2use crate::std::collections::BTreeMap as Map;
3#[cfg(features = "std")]
4use crate::std::collections::HashMap as Map;
5
6use crate::std::{num::NonZeroU32, str::FromStr};
7use parity_wasm::elements::Instruction;
8
9pub struct UnknownInstruction;
10
11/// An interface that describes instruction costs.
12pub trait Rules {
13	/// Returns the cost for the passed `instruction`.
14	///
15	/// Returning `None` makes the gas instrumention end with an error. This is meant
16	/// as a way to have a partial rule set where any instruction that is not specifed
17	/// is considered as forbidden.
18	fn instruction_cost(&self, instruction: &Instruction) -> Option<u32>;
19
20	/// Returns the costs for growing the memory using the `memory.grow` instruction.
21	///
22	/// Please note that these costs are in addition to the costs specified by `instruction_cost`
23	/// for the `memory.grow` instruction. Specifying `None` leads to no additional charge.
24	/// Those are meant as dynamic costs which take the amount of pages that the memory is
25	/// grown by into consideration. This is not possible using `instruction_cost` because
26	/// those costs depend on the stack and must be injected as code into the function calling
27	/// `memory.grow`. Therefore returning `Some` comes with a performance cost.
28	fn memory_grow_cost(&self) -> Option<MemoryGrowCost>;
29}
30
31/// Dynamic costs for memory growth.
32#[derive(Debug, PartialEq, Eq, Copy, Clone)]
33pub enum MemoryGrowCost {
34	/// Charge the specified amount for each page that the memory is grown by.
35	Linear(NonZeroU32),
36}
37
38#[derive(Debug, PartialEq, Eq, Copy, Clone)]
39pub enum Metering {
40	Regular,
41	Forbidden,
42	Fixed(u32),
43}
44
45#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
46pub enum InstructionType {
47	Bit,
48	Add,
49	Mul,
50	Div,
51	Load,
52	Store,
53	Const,
54	FloatConst,
55	Local,
56	Global,
57	ControlFlow,
58	IntegerComparison,
59	FloatComparison,
60	Float,
61	Conversion,
62	FloatConversion,
63	Reinterpretation,
64	Unreachable,
65	Nop,
66	CurrentMemory,
67	GrowMemory,
68
69	#[cfg(feature = "sign_ext")]
70	SignExt,
71}
72
73impl FromStr for InstructionType {
74	type Err = UnknownInstruction;
75
76	fn from_str(s: &str) -> Result<Self, Self::Err> {
77		match s {
78			"bit" => Ok(InstructionType::Bit),
79			"add" => Ok(InstructionType::Add),
80			"mul" => Ok(InstructionType::Mul),
81			"div" => Ok(InstructionType::Div),
82			"load" => Ok(InstructionType::Load),
83			"store" => Ok(InstructionType::Store),
84			"const" => Ok(InstructionType::Const),
85			"local" => Ok(InstructionType::Local),
86			"global" => Ok(InstructionType::Global),
87			"flow" => Ok(InstructionType::ControlFlow),
88			"integer_comp" => Ok(InstructionType::IntegerComparison),
89			"float_comp" => Ok(InstructionType::FloatComparison),
90			"float" => Ok(InstructionType::Float),
91			"conversion" => Ok(InstructionType::Conversion),
92			"float_conversion" => Ok(InstructionType::FloatConversion),
93			"reinterpret" => Ok(InstructionType::Reinterpretation),
94			"unreachable" => Ok(InstructionType::Unreachable),
95			"nop" => Ok(InstructionType::Nop),
96			"current_mem" => Ok(InstructionType::CurrentMemory),
97			"grow_mem" => Ok(InstructionType::GrowMemory),
98
99			#[cfg(feature = "sign_ext")]
100			"sign_ext" => Ok(InstructionType::SignExt),
101
102			_ => Err(UnknownInstruction),
103		}
104	}
105}
106
107impl InstructionType {
108	pub fn op(instruction: &Instruction) -> Self {
109		use Instruction::*;
110
111		match *instruction {
112			Unreachable => InstructionType::Unreachable,
113			Nop => InstructionType::Nop,
114			Block(_) => InstructionType::ControlFlow,
115			Loop(_) => InstructionType::ControlFlow,
116			If(_) => InstructionType::ControlFlow,
117			Else => InstructionType::ControlFlow,
118			End => InstructionType::ControlFlow,
119			Br(_) => InstructionType::ControlFlow,
120			BrIf(_) => InstructionType::ControlFlow,
121			BrTable(_) => InstructionType::ControlFlow,
122			Return => InstructionType::ControlFlow,
123			Call(_) => InstructionType::ControlFlow,
124			CallIndirect(_, _) => InstructionType::ControlFlow,
125			Drop => InstructionType::ControlFlow,
126			Select => InstructionType::ControlFlow,
127
128			GetLocal(_) => InstructionType::Local,
129			SetLocal(_) => InstructionType::Local,
130			TeeLocal(_) => InstructionType::Local,
131			GetGlobal(_) => InstructionType::Global,
132			SetGlobal(_) => InstructionType::Global,
133
134			I32Load(_, _) => InstructionType::Load,
135			I64Load(_, _) => InstructionType::Load,
136			F32Load(_, _) => InstructionType::Load,
137			F64Load(_, _) => InstructionType::Load,
138			I32Load8S(_, _) => InstructionType::Load,
139			I32Load8U(_, _) => InstructionType::Load,
140			I32Load16S(_, _) => InstructionType::Load,
141			I32Load16U(_, _) => InstructionType::Load,
142			I64Load8S(_, _) => InstructionType::Load,
143			I64Load8U(_, _) => InstructionType::Load,
144			I64Load16S(_, _) => InstructionType::Load,
145			I64Load16U(_, _) => InstructionType::Load,
146			I64Load32S(_, _) => InstructionType::Load,
147			I64Load32U(_, _) => InstructionType::Load,
148
149			I32Store(_, _) => InstructionType::Store,
150			I64Store(_, _) => InstructionType::Store,
151			F32Store(_, _) => InstructionType::Store,
152			F64Store(_, _) => InstructionType::Store,
153			I32Store8(_, _) => InstructionType::Store,
154			I32Store16(_, _) => InstructionType::Store,
155			I64Store8(_, _) => InstructionType::Store,
156			I64Store16(_, _) => InstructionType::Store,
157			I64Store32(_, _) => InstructionType::Store,
158
159			CurrentMemory(_) => InstructionType::CurrentMemory,
160			GrowMemory(_) => InstructionType::GrowMemory,
161
162			I32Const(_) => InstructionType::Const,
163			I64Const(_) => InstructionType::Const,
164
165			F32Const(_) => InstructionType::FloatConst,
166			F64Const(_) => InstructionType::FloatConst,
167
168			I32Eqz => InstructionType::IntegerComparison,
169			I32Eq => InstructionType::IntegerComparison,
170			I32Ne => InstructionType::IntegerComparison,
171			I32LtS => InstructionType::IntegerComparison,
172			I32LtU => InstructionType::IntegerComparison,
173			I32GtS => InstructionType::IntegerComparison,
174			I32GtU => InstructionType::IntegerComparison,
175			I32LeS => InstructionType::IntegerComparison,
176			I32LeU => InstructionType::IntegerComparison,
177			I32GeS => InstructionType::IntegerComparison,
178			I32GeU => InstructionType::IntegerComparison,
179
180			I64Eqz => InstructionType::IntegerComparison,
181			I64Eq => InstructionType::IntegerComparison,
182			I64Ne => InstructionType::IntegerComparison,
183			I64LtS => InstructionType::IntegerComparison,
184			I64LtU => InstructionType::IntegerComparison,
185			I64GtS => InstructionType::IntegerComparison,
186			I64GtU => InstructionType::IntegerComparison,
187			I64LeS => InstructionType::IntegerComparison,
188			I64LeU => InstructionType::IntegerComparison,
189			I64GeS => InstructionType::IntegerComparison,
190			I64GeU => InstructionType::IntegerComparison,
191
192			F32Eq => InstructionType::FloatComparison,
193			F32Ne => InstructionType::FloatComparison,
194			F32Lt => InstructionType::FloatComparison,
195			F32Gt => InstructionType::FloatComparison,
196			F32Le => InstructionType::FloatComparison,
197			F32Ge => InstructionType::FloatComparison,
198
199			F64Eq => InstructionType::FloatComparison,
200			F64Ne => InstructionType::FloatComparison,
201			F64Lt => InstructionType::FloatComparison,
202			F64Gt => InstructionType::FloatComparison,
203			F64Le => InstructionType::FloatComparison,
204			F64Ge => InstructionType::FloatComparison,
205
206			I32Clz => InstructionType::Bit,
207			I32Ctz => InstructionType::Bit,
208			I32Popcnt => InstructionType::Bit,
209			I32Add => InstructionType::Add,
210			I32Sub => InstructionType::Add,
211			I32Mul => InstructionType::Mul,
212			I32DivS => InstructionType::Div,
213			I32DivU => InstructionType::Div,
214			I32RemS => InstructionType::Div,
215			I32RemU => InstructionType::Div,
216			I32And => InstructionType::Bit,
217			I32Or => InstructionType::Bit,
218			I32Xor => InstructionType::Bit,
219			I32Shl => InstructionType::Bit,
220			I32ShrS => InstructionType::Bit,
221			I32ShrU => InstructionType::Bit,
222			I32Rotl => InstructionType::Bit,
223			I32Rotr => InstructionType::Bit,
224
225			I64Clz => InstructionType::Bit,
226			I64Ctz => InstructionType::Bit,
227			I64Popcnt => InstructionType::Bit,
228			I64Add => InstructionType::Add,
229			I64Sub => InstructionType::Add,
230			I64Mul => InstructionType::Mul,
231			I64DivS => InstructionType::Div,
232			I64DivU => InstructionType::Div,
233			I64RemS => InstructionType::Div,
234			I64RemU => InstructionType::Div,
235			I64And => InstructionType::Bit,
236			I64Or => InstructionType::Bit,
237			I64Xor => InstructionType::Bit,
238			I64Shl => InstructionType::Bit,
239			I64ShrS => InstructionType::Bit,
240			I64ShrU => InstructionType::Bit,
241			I64Rotl => InstructionType::Bit,
242			I64Rotr => InstructionType::Bit,
243
244			F32Abs => InstructionType::Float,
245			F32Neg => InstructionType::Float,
246			F32Ceil => InstructionType::Float,
247			F32Floor => InstructionType::Float,
248			F32Trunc => InstructionType::Float,
249			F32Nearest => InstructionType::Float,
250			F32Sqrt => InstructionType::Float,
251			F32Add => InstructionType::Float,
252			F32Sub => InstructionType::Float,
253			F32Mul => InstructionType::Float,
254			F32Div => InstructionType::Float,
255			F32Min => InstructionType::Float,
256			F32Max => InstructionType::Float,
257			F32Copysign => InstructionType::Float,
258			F64Abs => InstructionType::Float,
259			F64Neg => InstructionType::Float,
260			F64Ceil => InstructionType::Float,
261			F64Floor => InstructionType::Float,
262			F64Trunc => InstructionType::Float,
263			F64Nearest => InstructionType::Float,
264			F64Sqrt => InstructionType::Float,
265			F64Add => InstructionType::Float,
266			F64Sub => InstructionType::Float,
267			F64Mul => InstructionType::Float,
268			F64Div => InstructionType::Float,
269			F64Min => InstructionType::Float,
270			F64Max => InstructionType::Float,
271			F64Copysign => InstructionType::Float,
272
273			I32WrapI64 => InstructionType::Conversion,
274			I64ExtendSI32 => InstructionType::Conversion,
275			I64ExtendUI32 => InstructionType::Conversion,
276
277			I32TruncSF32 => InstructionType::FloatConversion,
278			I32TruncUF32 => InstructionType::FloatConversion,
279			I32TruncSF64 => InstructionType::FloatConversion,
280			I32TruncUF64 => InstructionType::FloatConversion,
281			I64TruncSF32 => InstructionType::FloatConversion,
282			I64TruncUF32 => InstructionType::FloatConversion,
283			I64TruncSF64 => InstructionType::FloatConversion,
284			I64TruncUF64 => InstructionType::FloatConversion,
285			F32ConvertSI32 => InstructionType::FloatConversion,
286			F32ConvertUI32 => InstructionType::FloatConversion,
287			F32ConvertSI64 => InstructionType::FloatConversion,
288			F32ConvertUI64 => InstructionType::FloatConversion,
289			F32DemoteF64 => InstructionType::FloatConversion,
290			F64ConvertSI32 => InstructionType::FloatConversion,
291			F64ConvertUI32 => InstructionType::FloatConversion,
292			F64ConvertSI64 => InstructionType::FloatConversion,
293			F64ConvertUI64 => InstructionType::FloatConversion,
294			F64PromoteF32 => InstructionType::FloatConversion,
295
296			I32ReinterpretF32 => InstructionType::Reinterpretation,
297			I64ReinterpretF64 => InstructionType::Reinterpretation,
298			F32ReinterpretI32 => InstructionType::Reinterpretation,
299			F64ReinterpretI64 => InstructionType::Reinterpretation,
300
301			#[cfg(feature = "sign_ext")]
302			SignExt(_) => InstructionType::SignExt,
303		}
304	}
305}
306
307#[derive(Debug)]
308pub struct Set {
309	regular: u32,
310	entries: Map<InstructionType, Metering>,
311	grow: u32,
312}
313
314impl Default for Set {
315	fn default() -> Self {
316		Set { regular: 1, entries: Map::new(), grow: 0 }
317	}
318}
319
320impl Set {
321	pub fn new(regular: u32, entries: Map<InstructionType, Metering>) -> Self {
322		Set { regular, entries, grow: 0 }
323	}
324
325	pub fn grow_cost(&self) -> u32 {
326		self.grow
327	}
328
329	pub fn with_grow_cost(mut self, val: u32) -> Self {
330		self.grow = val;
331		self
332	}
333
334	pub fn with_forbidden_floats(mut self) -> Self {
335		self.entries.insert(InstructionType::Float, Metering::Forbidden);
336		self.entries.insert(InstructionType::FloatComparison, Metering::Forbidden);
337		self.entries.insert(InstructionType::FloatConst, Metering::Forbidden);
338		self.entries.insert(InstructionType::FloatConversion, Metering::Forbidden);
339		self
340	}
341}
342
343impl Rules for Set {
344	fn instruction_cost(&self, instruction: &Instruction) -> Option<u32> {
345		match self.entries.get(&InstructionType::op(instruction)) {
346			None | Some(Metering::Regular) => Some(self.regular),
347			Some(Metering::Fixed(val)) => Some(*val),
348			Some(Metering::Forbidden) => None,
349		}
350	}
351
352	fn memory_grow_cost(&self) -> Option<MemoryGrowCost> {
353		NonZeroU32::new(self.grow).map(MemoryGrowCost::Linear)
354	}
355}