twasm_utils/
rules.rs

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