1use crate::code_translator::{bitcast_wasm_returns, translate_operator};
8use crate::environ::FuncEnvironment;
9use crate::state::FuncTranslationState;
10use crate::translation_utils::get_vmctx_value_label;
11use crate::WasmResult;
12use core::convert::TryInto;
13use cranelift_codegen::entity::EntityRef;
14use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
15use cranelift_codegen::timing;
16use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
17use wasmparser::{self, BinaryReader, FuncValidator, FunctionBody, WasmModuleResources};
18
19pub struct FuncTranslator {
25 func_ctx: FunctionBuilderContext,
26 state: FuncTranslationState,
27}
28
29impl FuncTranslator {
30 pub fn new() -> Self {
32 Self {
33 func_ctx: FunctionBuilderContext::new(),
34 state: FuncTranslationState::new(),
35 }
36 }
37
38 pub fn context(&mut self) -> &mut FunctionBuilderContext {
41 &mut self.func_ctx
42 }
43
44 pub fn translate<FE: FuncEnvironment + ?Sized>(
63 &mut self,
64 validator: &mut FuncValidator<impl WasmModuleResources>,
65 code: &[u8],
66 code_offset: usize,
67 func: &mut ir::Function,
68 environ: &mut FE,
69 ) -> WasmResult<()> {
70 self.translate_body(
71 validator,
72 FunctionBody::new(code_offset, code),
73 func,
74 environ,
75 )
76 }
77
78 pub fn translate_body<FE: FuncEnvironment + ?Sized>(
80 &mut self,
81 validator: &mut FuncValidator<impl WasmModuleResources>,
82 body: FunctionBody<'_>,
83 func: &mut ir::Function,
84 environ: &mut FE,
85 ) -> WasmResult<()> {
86 let _tt = timing::wasm_translate_function();
87 let mut reader = body.get_binary_reader();
88 log::trace!(
89 "translate({} bytes, {}{})",
90 reader.bytes_remaining(),
91 func.name,
92 func.signature
93 );
94 debug_assert_eq!(func.dfg.num_blocks(), 0, "Function must be empty");
95 debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
96
97 let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
98 builder.set_srcloc(cur_srcloc(&reader));
99 let entry_block = builder.create_block();
100 builder.append_block_params_for_function_params(entry_block);
101 builder.switch_to_block(entry_block);
102 builder.seal_block(entry_block); builder.ensure_inserted_block();
107
108 let num_params = declare_wasm_parameters(&mut builder, entry_block, environ);
109
110 let exit_block = builder.create_block();
113 builder.append_block_params_for_function_returns(exit_block);
114 self.state.initialize(&builder.func.signature, exit_block);
115
116 parse_local_decls(&mut reader, &mut builder, num_params, environ, validator)?;
117 parse_function_body(validator, reader, &mut builder, &mut self.state, environ)?;
118
119 builder.finalize();
120 log::trace!("translated Wasm to CLIF:\n{}", func.display());
121 Ok(())
122 }
123}
124
125fn declare_wasm_parameters<FE: FuncEnvironment + ?Sized>(
129 builder: &mut FunctionBuilder,
130 entry_block: Block,
131 environ: &FE,
132) -> usize {
133 let sig_len = builder.func.signature.params.len();
134 let mut next_local = 0;
135 for i in 0..sig_len {
136 let param_type = builder.func.signature.params[i];
137 if environ.is_wasm_parameter(&builder.func.signature, i) {
140 let local = Variable::new(next_local);
142 builder.declare_var(local, param_type.value_type);
143 next_local += 1;
144
145 let param_value = builder.block_params(entry_block)[i];
146 builder.def_var(local, param_value);
147 }
148 if param_type.purpose == ir::ArgumentPurpose::VMContext {
149 let param_value = builder.block_params(entry_block)[i];
150 builder.set_val_label(param_value, get_vmctx_value_label());
151 }
152 }
153
154 next_local
155}
156
157fn parse_local_decls<FE: FuncEnvironment + ?Sized>(
161 reader: &mut BinaryReader,
162 builder: &mut FunctionBuilder,
163 num_params: usize,
164 environ: &mut FE,
165 validator: &mut FuncValidator<impl WasmModuleResources>,
166) -> WasmResult<()> {
167 let mut next_local = num_params;
168 let local_count = reader.read_var_u32()?;
169
170 for _ in 0..local_count {
171 builder.set_srcloc(cur_srcloc(reader));
172 let pos = reader.original_position();
173 let count = reader.read_var_u32()?;
174 let ty = reader.read()?;
175 validator.define_locals(pos, count, ty)?;
176 declare_locals(builder, count, ty, &mut next_local, environ)?;
177 }
178
179 environ.after_locals(next_local);
180
181 Ok(())
182}
183
184fn declare_locals<FE: FuncEnvironment + ?Sized>(
188 builder: &mut FunctionBuilder,
189 count: u32,
190 wasm_type: wasmparser::ValType,
191 next_local: &mut usize,
192 environ: &mut FE,
193) -> WasmResult<()> {
194 use wasmparser::ValType::*;
196 let zeroval = match wasm_type {
197 I32 => builder.ins().iconst(ir::types::I32, 0),
198 I64 => builder.ins().iconst(ir::types::I64, 0),
199 F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
200 F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
201 V128 => {
202 let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
203 builder.ins().vconst(ir::types::I8X16, constant_handle)
204 }
205 Ref(wasmparser::RefType {
206 nullable: true,
207 heap_type,
208 }) => environ.translate_ref_null(builder.cursor(), heap_type.try_into()?)?,
209 Ref(wasmparser::RefType {
210 nullable: false, ..
211 }) => unreachable!(),
212 };
213
214 let ty = builder.func.dfg.value_type(zeroval);
215 for _ in 0..count {
216 let local = Variable::new(*next_local);
217 builder.declare_var(local, ty);
218 builder.def_var(local, zeroval);
219 builder.set_val_label(zeroval, ValueLabel::new(*next_local));
220 *next_local += 1;
221 }
222 Ok(())
223}
224
225fn parse_function_body<FE: FuncEnvironment + ?Sized>(
230 validator: &mut FuncValidator<impl WasmModuleResources>,
231 mut reader: BinaryReader,
232 builder: &mut FunctionBuilder,
233 state: &mut FuncTranslationState,
234 environ: &mut FE,
235) -> WasmResult<()> {
236 debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
238
239 environ.before_translate_function(builder, state)?;
240 while !reader.eof() {
241 let pos = reader.original_position();
242 builder.set_srcloc(cur_srcloc(&reader));
243 let op = reader.read_operator()?;
244 validator.op(pos, &op)?;
245 environ.before_translate_operator(&op, builder, state)?;
246 translate_operator(validator, &op, builder, state, environ)?;
247 environ.after_translate_operator(&op, builder, state)?;
248 }
249 environ.after_translate_function(builder, state)?;
250 let pos = reader.original_position();
251 validator.finish(pos)?;
252
253 if state.reachable {
259 if !builder.is_unreachable() {
260 bitcast_wasm_returns(environ, &mut state.stack, builder);
261 builder.ins().return_(&state.stack);
262 }
263 }
264
265 state.stack.clear();
268
269 Ok(())
270}
271
272fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
274 ir::SourceLoc::new(reader.original_position() as u32)
277}
278
279#[cfg(test)]
280mod tests {
281 use super::FuncTranslator;
282 use crate::environ::DummyEnvironment;
283 use cranelift_codegen::ir::types::I32;
284 use cranelift_codegen::{ir, isa, settings, Context};
285 use log::debug;
286 use target_lexicon::PointerWidth;
287 use wasmparser::{
288 FuncValidator, FunctionBody, Parser, ValidPayload, Validator, ValidatorResources,
289 };
290
291 #[test]
292 fn small1() {
293 let wasm = wat::parse_str(
295 "
296 (module
297 (func $small2 (param i32) (result i32)
298 (i32.add (get_local 0) (i32.const 1))
299 )
300 )
301 ",
302 )
303 .unwrap();
304
305 let mut trans = FuncTranslator::new();
306 let flags = settings::Flags::new(settings::builder());
307 let runtime = DummyEnvironment::new(
308 isa::TargetFrontendConfig {
309 default_call_conv: isa::CallConv::Fast,
310 pointer_width: PointerWidth::U64,
311 },
312 false,
313 );
314
315 let mut ctx = Context::new();
316
317 ctx.func.name = ir::UserFuncName::testcase("small1");
318 ctx.func.signature.params.push(ir::AbiParam::new(I32));
319 ctx.func.signature.returns.push(ir::AbiParam::new(I32));
320
321 let (body, mut validator) = extract_func(&wasm);
322 trans
323 .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
324 .unwrap();
325 debug!("{}", ctx.func.display());
326 ctx.verify(&flags).unwrap();
327 }
328
329 #[test]
330 fn small2() {
331 let wasm = wat::parse_str(
333 "
334 (module
335 (func $small2 (param i32) (result i32)
336 (return (i32.add (get_local 0) (i32.const 1)))
337 )
338 )
339 ",
340 )
341 .unwrap();
342
343 let mut trans = FuncTranslator::new();
344 let flags = settings::Flags::new(settings::builder());
345 let runtime = DummyEnvironment::new(
346 isa::TargetFrontendConfig {
347 default_call_conv: isa::CallConv::Fast,
348 pointer_width: PointerWidth::U64,
349 },
350 false,
351 );
352
353 let mut ctx = Context::new();
354
355 ctx.func.name = ir::UserFuncName::testcase("small2");
356 ctx.func.signature.params.push(ir::AbiParam::new(I32));
357 ctx.func.signature.returns.push(ir::AbiParam::new(I32));
358
359 let (body, mut validator) = extract_func(&wasm);
360 trans
361 .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
362 .unwrap();
363 debug!("{}", ctx.func.display());
364 ctx.verify(&flags).unwrap();
365 }
366
367 #[test]
368 fn infloop() {
369 let wasm = wat::parse_str(
371 "
372 (module
373 (func $infloop (result i32)
374 (local i32)
375 (loop (result i32)
376 (i32.add (get_local 0) (i32.const 1))
377 (set_local 0)
378 (br 0)
379 )
380 )
381 )
382 ",
383 )
384 .unwrap();
385
386 let mut trans = FuncTranslator::new();
387 let flags = settings::Flags::new(settings::builder());
388 let runtime = DummyEnvironment::new(
389 isa::TargetFrontendConfig {
390 default_call_conv: isa::CallConv::Fast,
391 pointer_width: PointerWidth::U64,
392 },
393 false,
394 );
395
396 let mut ctx = Context::new();
397
398 ctx.func.name = ir::UserFuncName::testcase("infloop");
399 ctx.func.signature.returns.push(ir::AbiParam::new(I32));
400
401 let (body, mut validator) = extract_func(&wasm);
402 trans
403 .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
404 .unwrap();
405 debug!("{}", ctx.func.display());
406 ctx.verify(&flags).unwrap();
407 }
408
409 fn extract_func(wat: &[u8]) -> (FunctionBody<'_>, FuncValidator<ValidatorResources>) {
410 let mut validator = Validator::new();
411 for payload in Parser::new(0).parse_all(wat) {
412 match validator.payload(&payload.unwrap()).unwrap() {
413 ValidPayload::Func(validator, body) => {
414 let validator = validator.into_validator(Default::default());
415 return (body, validator);
416 }
417 _ => {}
418 }
419 }
420 panic!("failed to find function");
421 }
422}