1 /* Copyright 2018 Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 // Safe wrappers to the low-level ABI. This re-exports all types in low_level but none of the
17 // functions.
18
19 use std::{mem, slice};
20
21 use cranelift_codegen::binemit::CodeOffset;
22 use cranelift_codegen::cursor::FuncCursor;
23 use cranelift_codegen::entity::EntityRef;
24 use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};
25 use cranelift_codegen::ir::{self, InstBuilder, SourceLoc};
26 use cranelift_codegen::isa;
27
28 use cranelift_wasm::{
29 wasmparser, FuncIndex, GlobalIndex, SignatureIndex, TableIndex, TypeIndex, WasmResult,
30 };
31
32 use crate::compile;
33 use crate::utils::BasicError;
34 use crate::wasm2clif::REF_TYPE;
35
36 use self::low_level::*;
37
38 pub use self::low_level::BD_SymbolicAddress as SymbolicAddress;
39 pub use self::low_level::CraneliftCompiledFunc as CompiledFunc;
40 pub use self::low_level::CraneliftFuncCompileInput as FuncCompileInput;
41 pub use self::low_level::CraneliftMetadataEntry as MetadataEntry;
42 pub use self::low_level::CraneliftModuleEnvironment as LowLevelModuleEnvironment;
43 pub use self::low_level::CraneliftStaticEnvironment as StaticEnvironment;
44 pub use self::low_level::Trap;
45 pub use self::low_level::TypeIdDescKind;
46
47 mod low_level;
48
49 /// Converts a `TypeCode` into the equivalent Cranelift type, if it's a known type, or an error
50 /// otherwise.
51 #[inline]
typecode_to_type(type_code: TypeCode) -> WasmResult<Option<ir::Type>>52 fn typecode_to_type(type_code: TypeCode) -> WasmResult<Option<ir::Type>> {
53 match type_code {
54 TypeCode::I32 => Ok(Some(ir::types::I32)),
55 TypeCode::I64 => Ok(Some(ir::types::I64)),
56 TypeCode::F32 => Ok(Some(ir::types::F32)),
57 TypeCode::F64 => Ok(Some(ir::types::F64)),
58 TypeCode::V128 => Ok(Some(ir::types::I8X16)),
59 TypeCode::FuncRef => Ok(Some(REF_TYPE)),
60 TypeCode::ExternRef => Ok(Some(REF_TYPE)),
61 TypeCode::BlockVoid => Ok(None),
62 _ => Err(BasicError::new(format!("unknown type code: {:?}", type_code)).into()),
63 }
64 }
65
66 /// Convert a non-void `TypeCode` into the equivalent Cranelift type.
67 #[inline]
typecode_to_nonvoid_type(type_code: TypeCode) -> WasmResult<ir::Type>68 pub(crate) fn typecode_to_nonvoid_type(type_code: TypeCode) -> WasmResult<ir::Type> {
69 Ok(typecode_to_type(type_code)?.expect("unexpected void type"))
70 }
71
72 /// Convert a u32 into a `BD_SymbolicAddress`.
73 impl From<u32> for SymbolicAddress {
from(x: u32) -> SymbolicAddress74 fn from(x: u32) -> SymbolicAddress {
75 assert!(x < SymbolicAddress::Limit as u32);
76 unsafe { mem::transmute(x) }
77 }
78 }
79
80 #[derive(Clone, Copy)]
81 pub struct GlobalDesc(*const low_level::GlobalDesc);
82
83 impl GlobalDesc {
value_type(self) -> WasmResult<ir::Type>84 pub fn value_type(self) -> WasmResult<ir::Type> {
85 let type_code = unsafe { low_level::global_type(self.0) };
86 typecode_to_nonvoid_type(type_code)
87 }
88
is_constant(self) -> bool89 pub fn is_constant(self) -> bool {
90 unsafe { low_level::global_isConstant(self.0) }
91 }
92
is_mutable(self) -> bool93 pub fn is_mutable(self) -> bool {
94 unsafe { low_level::global_isMutable(self.0) }
95 }
96
is_indirect(self) -> bool97 pub fn is_indirect(self) -> bool {
98 unsafe { low_level::global_isIndirect(self.0) }
99 }
100
101 /// Insert an instruction at `pos` that materializes the constant value.
emit_constant(self, pos: &mut FuncCursor) -> WasmResult<ir::Value>102 pub fn emit_constant(self, pos: &mut FuncCursor) -> WasmResult<ir::Value> {
103 unsafe {
104 let v = low_level::global_constantValue(self.0);
105 match v.t {
106 TypeCode::I32 => Ok(pos.ins().iconst(ir::types::I32, i64::from(v.u.i32_))),
107 TypeCode::I64 => Ok(pos.ins().iconst(ir::types::I64, v.u.i64_)),
108 TypeCode::F32 => Ok(pos.ins().f32const(Ieee32::with_bits(v.u.i32_ as u32))),
109 TypeCode::F64 => Ok(pos.ins().f64const(Ieee64::with_bits(v.u.i64_ as u64))),
110 TypeCode::V128 => {
111 let c = pos
112 .func
113 .dfg
114 .constants
115 .insert(ir::ConstantData::from(&v.u.v128 as &[u8]));
116 Ok(pos.ins().vconst(ir::types::I8X16, c))
117 }
118 TypeCode::NullableRef | TypeCode::ExternRef | TypeCode::FuncRef => {
119 assert!(v.u.r as usize == 0);
120 Ok(pos.ins().null(REF_TYPE))
121 }
122 _ => Err(BasicError::new(format!("unexpected type: {}", v.t as u64)).into()),
123 }
124 }
125 }
126
127 /// Get the offset from the `WasmTlsReg` to the memory representing this global variable.
tls_offset(self) -> usize128 pub fn tls_offset(self) -> usize {
129 unsafe { low_level::global_tlsOffset(self.0) }
130 }
131
content_type(self) -> wasmparser::Type132 pub fn content_type(self) -> wasmparser::Type {
133 typecode_to_parser_type(unsafe { low_level::global_type(self.0) })
134 }
135 }
136
137 #[derive(Clone, Copy)]
138 pub struct TableDesc(*const low_level::TableDesc);
139
140 impl TableDesc {
141 /// Get the offset from the `WasmTlsReg` to the `wasm::TableTls` representing this table.
tls_offset(self) -> usize142 pub fn tls_offset(self) -> usize {
143 unsafe { low_level::table_tlsOffset(self.0) }
144 }
145
element_type(self) -> wasmparser::Type146 pub fn element_type(self) -> wasmparser::Type {
147 typecode_to_parser_type(unsafe { low_level::table_elementTypeCode(self.0) })
148 }
149
resizable_limits(self) -> wasmparser::ResizableLimits150 pub fn resizable_limits(self) -> wasmparser::ResizableLimits {
151 let initial = unsafe { low_level::table_initialLimit(self.0) };
152 let maximum = unsafe { low_level::table_initialLimit(self.0) };
153 let maximum = if maximum == u32::max_value() {
154 None
155 } else {
156 Some(maximum)
157 };
158 wasmparser::ResizableLimits { initial, maximum }
159 }
160 }
161
162 #[derive(Clone)]
163 pub struct FuncType {
164 ptr: *const low_level::FuncType,
165 args: Vec<TypeCode>,
166 results: Vec<TypeCode>,
167 }
168
169 impl FuncType {
170 /// Creates a new FuncType, caching all the values it requires.
new(ptr: *const low_level::FuncType) -> Self171 pub(crate) fn new(ptr: *const low_level::FuncType) -> Self {
172 let num_args = unsafe { low_level::funcType_numArgs(ptr) };
173 let args = unsafe { slice::from_raw_parts(low_level::funcType_args(ptr), num_args) };
174 let args = args
175 .iter()
176 .map(|val_type| unsafe { low_level::env_unpack(*val_type) })
177 .collect();
178
179 let num_results = unsafe { low_level::funcType_numResults(ptr) };
180 let results =
181 unsafe { slice::from_raw_parts(low_level::funcType_results(ptr), num_results) };
182 let results = results
183 .iter()
184 .map(|val_type| unsafe { low_level::env_unpack(*val_type) })
185 .collect();
186
187 Self { ptr, args, results }
188 }
189
args(&self) -> &[TypeCode]190 pub(crate) fn args(&self) -> &[TypeCode] {
191 &self.args
192 }
results(&self) -> &[TypeCode]193 pub(crate) fn results(&self) -> &[TypeCode] {
194 &self.results
195 }
196 }
197
198 #[derive(Clone)]
199 pub struct TypeIdDesc {
200 ptr: *const low_level::TypeIdDesc,
201 }
202
203 impl TypeIdDesc {
new(ptr: *const low_level::TypeIdDesc) -> Self204 pub(crate) fn new(ptr: *const low_level::TypeIdDesc) -> Self {
205 Self { ptr }
206 }
207
id_kind(&self) -> TypeIdDescKind208 pub(crate) fn id_kind(&self) -> TypeIdDescKind {
209 unsafe { low_level::funcType_idKind(self.ptr) }
210 }
id_immediate(&self) -> usize211 pub(crate) fn id_immediate(&self) -> usize {
212 unsafe { low_level::funcType_idImmediate(self.ptr) }
213 }
id_tls_offset(&self) -> usize214 pub(crate) fn id_tls_offset(&self) -> usize {
215 unsafe { low_level::funcType_idTlsOffset(self.ptr) }
216 }
217 }
218
typecode_to_parser_type(ty: TypeCode) -> wasmparser::Type219 fn typecode_to_parser_type(ty: TypeCode) -> wasmparser::Type {
220 match ty {
221 TypeCode::I32 => wasmparser::Type::I32,
222 TypeCode::I64 => wasmparser::Type::I64,
223 TypeCode::F32 => wasmparser::Type::F32,
224 TypeCode::F64 => wasmparser::Type::F64,
225 TypeCode::V128 => wasmparser::Type::V128,
226 TypeCode::FuncRef => wasmparser::Type::FuncRef,
227 TypeCode::ExternRef => wasmparser::Type::ExternRef,
228 TypeCode::BlockVoid => wasmparser::Type::EmptyBlockType,
229 _ => panic!("unknown type code: {:?}", ty),
230 }
231 }
232
233 impl wasmparser::WasmFuncType for FuncType {
len_inputs(&self) -> usize234 fn len_inputs(&self) -> usize {
235 self.args.len()
236 }
len_outputs(&self) -> usize237 fn len_outputs(&self) -> usize {
238 self.results.len()
239 }
input_at(&self, at: u32) -> Option<wasmparser::Type>240 fn input_at(&self, at: u32) -> Option<wasmparser::Type> {
241 self.args
242 .get(at as usize)
243 .map(|ty| typecode_to_parser_type(*ty))
244 }
output_at(&self, at: u32) -> Option<wasmparser::Type>245 fn output_at(&self, at: u32) -> Option<wasmparser::Type> {
246 self.results
247 .get(at as usize)
248 .map(|ty| typecode_to_parser_type(*ty))
249 }
250 }
251
252 /// Thin wrapper for the CraneliftModuleEnvironment structure.
253
254 pub struct ModuleEnvironment<'a> {
255 env: &'a CraneliftModuleEnvironment,
256 /// The `WasmModuleResources` trait requires us to return a borrow to a `FuncType`, so we
257 /// eagerly construct these.
258 types: Vec<FuncType>,
259 /// Similar to `types`, we need to have a persistently-stored `FuncType` to return. The
260 /// types in `func_sigs` are a subset of those in `types`, but we don't want to have to
261 /// maintain an index from function to signature ID, so we store these directly.
262 func_sigs: Vec<FuncType>,
263 }
264
265 impl<'a> ModuleEnvironment<'a> {
new(env: &'a CraneliftModuleEnvironment) -> Self266 pub(crate) fn new(env: &'a CraneliftModuleEnvironment) -> Self {
267 let num_types = unsafe { low_level::env_num_types(env) };
268 let mut types = Vec::with_capacity(num_types);
269 for i in 0..num_types {
270 let t = FuncType::new(unsafe { low_level::env_signature(env, i) });
271 types.push(t);
272 }
273 let num_func_sigs = unsafe { low_level::env_num_funcs(env) };
274 let mut func_sigs = Vec::with_capacity(num_func_sigs);
275 for i in 0..num_func_sigs {
276 let t = FuncType::new(unsafe { low_level::env_func_sig(env, i) });
277 func_sigs.push(t);
278 }
279 Self {
280 env,
281 types,
282 func_sigs,
283 }
284 }
has_memory(&self) -> bool285 pub fn has_memory(&self) -> bool {
286 unsafe { low_level::env_has_memory(self.env) }
287 }
uses_shared_memory(&self) -> bool288 pub fn uses_shared_memory(&self) -> bool {
289 unsafe { low_level::env_uses_shared_memory(self.env) }
290 }
num_tables(&self) -> usize291 pub fn num_tables(&self) -> usize {
292 unsafe { low_level::env_num_tables(self.env) }
293 }
num_types(&self) -> usize294 pub fn num_types(&self) -> usize {
295 self.types.len()
296 }
type_(&self, index: usize) -> FuncType297 pub fn type_(&self, index: usize) -> FuncType {
298 self.types[index].clone()
299 }
num_func_sigs(&self) -> usize300 pub fn num_func_sigs(&self) -> usize {
301 self.func_sigs.len()
302 }
func_sig(&self, func_index: FuncIndex) -> FuncType303 pub fn func_sig(&self, func_index: FuncIndex) -> FuncType {
304 self.func_sigs[func_index.index()].clone()
305 }
func_sig_index(&self, func_index: FuncIndex) -> SignatureIndex306 pub fn func_sig_index(&self, func_index: FuncIndex) -> SignatureIndex {
307 SignatureIndex::new(unsafe { low_level::env_func_sig_index(self.env, func_index.index()) })
308 }
func_import_tls_offset(&self, func_index: FuncIndex) -> usize309 pub fn func_import_tls_offset(&self, func_index: FuncIndex) -> usize {
310 unsafe { low_level::env_func_import_tls_offset(self.env, func_index.index()) }
311 }
func_is_import(&self, func_index: FuncIndex) -> bool312 pub fn func_is_import(&self, func_index: FuncIndex) -> bool {
313 unsafe { low_level::env_func_is_import(self.env, func_index.index()) }
314 }
signature(&self, type_index: TypeIndex) -> FuncType315 pub fn signature(&self, type_index: TypeIndex) -> FuncType {
316 // This function takes `TypeIndex` rather than the `SignatureIndex` that one
317 // might expect. Why? https://github.com/bytecodealliance/wasmtime/pull/2115
318 // introduces two new types to the type section as viewed by Cranelift. This is
319 // in support of the module linking proposal. So now a type index (for
320 // Cranelift) can refer to a func, module, or instance type. When the type index
321 // refers to a func type, it can also be used to get the signature index which
322 // can be used to get the ir::Signature for that func type. For us, Cranelift is
323 // only used with function types so we can just assume type index and signature
324 // index are 1:1. If and when we come to support the module linking proposal,
325 // this will need to be revisited.
326 FuncType::new(unsafe { low_level::env_signature(self.env, type_index.index()) })
327 }
signature_id(&self, type_index: TypeIndex) -> TypeIdDesc328 pub fn signature_id(&self, type_index: TypeIndex) -> TypeIdDesc {
329 TypeIdDesc::new(unsafe { low_level::env_signature_id(self.env, type_index.index()) })
330 }
table(&self, table_index: TableIndex) -> TableDesc331 pub fn table(&self, table_index: TableIndex) -> TableDesc {
332 TableDesc(unsafe { low_level::env_table(self.env, table_index.index()) })
333 }
global(&self, global_index: GlobalIndex) -> GlobalDesc334 pub fn global(&self, global_index: GlobalIndex) -> GlobalDesc {
335 GlobalDesc(unsafe { low_level::env_global(self.env, global_index.index()) })
336 }
min_memory_length(&self) -> u32337 pub fn min_memory_length(&self) -> u32 {
338 self.env.min_memory_length
339 }
max_memory_length(&self) -> Option<u32>340 pub fn max_memory_length(&self) -> Option<u32> {
341 let max = unsafe { low_level::env_max_memory(self.env) };
342 if max == u32::max_value() {
343 None
344 } else {
345 Some(max)
346 }
347 }
348 }
349
350 impl<'module> wasmparser::WasmModuleResources for ModuleEnvironment<'module> {
351 type FuncType = FuncType;
table_at(&self, at: u32) -> Option<wasmparser::TableType>352 fn table_at(&self, at: u32) -> Option<wasmparser::TableType> {
353 if (at as usize) < self.num_tables() {
354 let desc = TableDesc(unsafe { low_level::env_table(self.env, at as usize) });
355 let element_type = desc.element_type();
356 let limits = desc.resizable_limits();
357 Some(wasmparser::TableType {
358 element_type,
359 limits,
360 })
361 } else {
362 None
363 }
364 }
memory_at(&self, at: u32) -> Option<wasmparser::MemoryType>365 fn memory_at(&self, at: u32) -> Option<wasmparser::MemoryType> {
366 if at == 0 {
367 let has_memory = self.has_memory();
368 if has_memory {
369 let shared = self.uses_shared_memory();
370 let initial = self.min_memory_length() as u32;
371 let maximum = self.max_memory_length();
372 Some(wasmparser::MemoryType::M32 {
373 limits: wasmparser::ResizableLimits { initial, maximum },
374 shared,
375 })
376 } else {
377 None
378 }
379 } else {
380 None
381 }
382 }
event_at(&self, _at: u32) -> Option<&Self::FuncType>383 fn event_at(&self, _at: u32) -> Option<&Self::FuncType> {
384 panic!("unexpected exception operation");
385 }
global_at(&self, at: u32) -> Option<wasmparser::GlobalType>386 fn global_at(&self, at: u32) -> Option<wasmparser::GlobalType> {
387 let num_globals = unsafe { low_level::env_num_globals(self.env) };
388 if (at as usize) < num_globals {
389 let desc = self.global(GlobalIndex::new(at as usize));
390 let mutable = desc.is_mutable();
391 let content_type = desc.content_type();
392 Some(wasmparser::GlobalType {
393 mutable,
394 content_type,
395 })
396 } else {
397 None
398 }
399 }
func_type_at(&self, type_idx: u32) -> Option<&Self::FuncType>400 fn func_type_at(&self, type_idx: u32) -> Option<&Self::FuncType> {
401 if (type_idx as usize) < self.types.len() {
402 Some(&self.types[type_idx as usize])
403 } else {
404 None
405 }
406 }
type_of_function(&self, func_idx: u32) -> Option<&Self::FuncType>407 fn type_of_function(&self, func_idx: u32) -> Option<&Self::FuncType> {
408 if (func_idx as usize) < self.func_sigs.len() {
409 Some(&self.func_sigs[func_idx as usize])
410 } else {
411 None
412 }
413 }
element_type_at(&self, at: u32) -> Option<wasmparser::Type>414 fn element_type_at(&self, at: u32) -> Option<wasmparser::Type> {
415 let num_elems = self.element_count();
416 if at < num_elems {
417 let elem_type = unsafe { low_level::env_elem_typecode(self.env, at) };
418 Some(typecode_to_parser_type(elem_type))
419 } else {
420 None
421 }
422 }
element_count(&self) -> u32423 fn element_count(&self) -> u32 {
424 unsafe { low_level::env_num_elems(self.env) as u32 }
425 }
data_count(&self) -> u32426 fn data_count(&self) -> u32 {
427 unsafe { low_level::env_num_datas(self.env) as u32 }
428 }
is_function_referenced(&self, idx: u32) -> bool429 fn is_function_referenced(&self, idx: u32) -> bool {
430 unsafe { low_level::env_is_func_valid_for_ref(self.env, idx) }
431 }
432 }
433
434 /// Extra methods for some C++ wrappers.
435
436 impl FuncCompileInput {
bytecode(&self) -> &[u8]437 pub fn bytecode(&self) -> &[u8] {
438 unsafe { slice::from_raw_parts(self.bytecode, self.bytecode_size) }
439 }
440
stackmaps(&self) -> Stackmaps441 pub fn stackmaps(&self) -> Stackmaps {
442 Stackmaps(self.stackmaps)
443 }
444 }
445
446 impl CompiledFunc {
reset(&mut self, compiled_func: &compile::CompiledFunc)447 pub fn reset(&mut self, compiled_func: &compile::CompiledFunc) {
448 self.num_metadata = compiled_func.metadata.len();
449 self.metadatas = compiled_func.metadata.as_ptr();
450
451 self.frame_pushed = compiled_func.frame_pushed as usize;
452 self.contains_calls = compiled_func.contains_calls;
453
454 self.code = compiled_func.code_buffer.as_ptr();
455 self.code_size = compiled_func.code_size as usize;
456 self.jumptables_size = compiled_func.jumptables_size as usize;
457 self.rodata_size = compiled_func.rodata_size as usize;
458 self.total_size = compiled_func.code_buffer.len();
459
460 self.num_rodata_relocs = compiled_func.rodata_relocs.len();
461 self.rodata_relocs = compiled_func.rodata_relocs.as_ptr();
462 }
463 }
464
465 impl MetadataEntry {
direct_call(code_offset: CodeOffset, srcloc: SourceLoc, func_index: FuncIndex) -> Self466 pub fn direct_call(code_offset: CodeOffset, srcloc: SourceLoc, func_index: FuncIndex) -> Self {
467 Self {
468 which: CraneliftMetadataEntry_Which_DirectCall,
469 code_offset,
470 module_bytecode_offset: srcloc.bits(),
471 extra: func_index.index(),
472 }
473 }
indirect_call(ret_addr: CodeOffset, srcloc: SourceLoc) -> Self474 pub fn indirect_call(ret_addr: CodeOffset, srcloc: SourceLoc) -> Self {
475 Self {
476 which: CraneliftMetadataEntry_Which_IndirectCall,
477 code_offset: ret_addr,
478 module_bytecode_offset: srcloc.bits(),
479 extra: 0,
480 }
481 }
trap(code_offset: CodeOffset, srcloc: SourceLoc, which: Trap) -> Self482 pub fn trap(code_offset: CodeOffset, srcloc: SourceLoc, which: Trap) -> Self {
483 Self {
484 which: CraneliftMetadataEntry_Which_Trap,
485 code_offset,
486 module_bytecode_offset: srcloc.bits(),
487 extra: which as usize,
488 }
489 }
symbolic_access( code_offset: CodeOffset, srcloc: SourceLoc, sym: SymbolicAddress, ) -> Self490 pub fn symbolic_access(
491 code_offset: CodeOffset,
492 srcloc: SourceLoc,
493 sym: SymbolicAddress,
494 ) -> Self {
495 Self {
496 which: CraneliftMetadataEntry_Which_SymbolicAccess,
497 code_offset,
498 module_bytecode_offset: srcloc.bits(),
499 extra: sym as usize,
500 }
501 }
502 }
503
504 impl StaticEnvironment {
505 /// Returns the default calling convention on this machine.
call_conv(&self) -> isa::CallConv506 pub fn call_conv(&self) -> isa::CallConv {
507 if self.platform_is_windows {
508 unimplemented!("No FastCall variant of Baldrdash2020")
509 } else {
510 isa::CallConv::Baldrdash2020
511 }
512 }
513 }
514
515 pub struct Stackmaps(*mut self::low_level::BD_Stackmaps);
516
517 impl Stackmaps {
add_stackmap( &mut self, inbound_args_size: u32, offset: CodeOffset, map: &cranelift_codegen::binemit::StackMap, )518 pub fn add_stackmap(
519 &mut self,
520 inbound_args_size: u32,
521 offset: CodeOffset,
522 map: &cranelift_codegen::binemit::StackMap,
523 ) {
524 unsafe {
525 let bitslice = map.as_slice();
526 low_level::stackmaps_add(
527 self.0,
528 std::mem::transmute(bitslice.as_ptr()),
529 map.mapped_words() as usize,
530 inbound_args_size as usize,
531 offset as usize,
532 );
533 }
534 }
535 }
536