Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/cranelift/src/debug/transform/address_transform.rs
1693 views
1
use crate::FunctionAddressMap;
2
use crate::debug::Compilation;
3
use gimli::write;
4
use std::collections::BTreeMap;
5
use wasmtime_environ::{DefinedFuncIndex, FilePos, PrimaryMap, StaticModuleIndex};
6
7
pub type GeneratedAddress = usize;
8
pub type WasmAddress = u64;
9
10
/// Contains mapping of the generated address to its original
11
/// source location.
12
#[derive(Debug)]
13
pub struct AddressMap {
14
pub generated: GeneratedAddress,
15
pub wasm: WasmAddress,
16
}
17
18
/// Information about generated function code: its body start,
19
/// length, and instructions addresses.
20
#[derive(Debug)]
21
pub struct FunctionMap {
22
pub symbol: usize,
23
pub offset: GeneratedAddress,
24
pub len: GeneratedAddress,
25
pub wasm_start: WasmAddress,
26
pub wasm_end: WasmAddress,
27
pub addresses: Box<[AddressMap]>,
28
}
29
30
/// Mapping of the source location to its generated code range.
31
#[derive(Debug)]
32
struct Position {
33
wasm_pos: WasmAddress,
34
gen_start: GeneratedAddress,
35
gen_end: GeneratedAddress,
36
}
37
38
/// Mapping of continuous range of source location to its generated
39
/// code. The positions are always in ascending order for search.
40
#[derive(Debug)]
41
struct Range {
42
wasm_start: WasmAddress,
43
wasm_end: WasmAddress,
44
gen_start: GeneratedAddress,
45
gen_end: GeneratedAddress,
46
positions: Box<[Position]>,
47
}
48
49
type RangeIndex = usize;
50
51
/// Helper function address lookup data. Contains ranges start positions
52
/// index and ranges data. The multiple ranges can include the same
53
/// original source position. The index (B-Tree) uses range start
54
/// position as a key. The index values reference the ranges array.
55
/// The item are ordered RangeIndex.
56
#[derive(Debug)]
57
struct FuncLookup {
58
index: Vec<(WasmAddress, Box<[RangeIndex]>)>,
59
ranges: Box<[Range]>,
60
}
61
62
/// Mapping of original functions to generated code locations/ranges.
63
#[derive(Debug)]
64
struct FuncTransform {
65
start: WasmAddress,
66
end: WasmAddress,
67
index: DefinedFuncIndex,
68
lookup: FuncLookup,
69
}
70
71
/// Module functions mapping to generated code.
72
#[derive(Debug)]
73
pub struct AddressTransform {
74
map: PrimaryMap<DefinedFuncIndex, FunctionMap>,
75
func: Vec<(WasmAddress, FuncTransform)>,
76
}
77
78
/// Returns a wasm bytecode offset in the code section from SourceLoc.
79
fn get_wasm_code_offset(loc: FilePos, code_section_offset: u64) -> WasmAddress {
80
// Code section size <= 4GB, allow wrapped SourceLoc to recover the overflow.
81
loc.file_offset()
82
.unwrap()
83
.wrapping_sub(code_section_offset as u32) as WasmAddress
84
}
85
86
fn build_function_lookup(
87
ft: &FunctionAddressMap,
88
code_section_offset: u64,
89
) -> (WasmAddress, WasmAddress, FuncLookup) {
90
assert!(code_section_offset <= ft.start_srcloc.file_offset().unwrap().into());
91
let fn_start = get_wasm_code_offset(ft.start_srcloc, code_section_offset);
92
let fn_end = get_wasm_code_offset(ft.end_srcloc, code_section_offset);
93
assert!(fn_start <= fn_end);
94
95
// Build ranges of continuous source locations. The new ranges starts when
96
// non-descending order is interrupted. Assuming the same origin location can
97
// be present in multiple ranges.
98
let mut range_wasm_start = fn_start;
99
let mut range_gen_start = ft.body_offset;
100
let mut last_wasm_pos = range_wasm_start;
101
let mut ranges = Vec::new();
102
let mut ranges_index = BTreeMap::new();
103
let mut current_range = Vec::new();
104
let mut last_gen_inst_empty = false;
105
for (i, t) in ft.instructions.iter().enumerate() {
106
if t.srcloc.file_offset().is_none() {
107
continue;
108
}
109
110
let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
111
assert!(fn_start <= offset);
112
assert!(offset <= fn_end);
113
114
let inst_gen_start = t.code_offset as usize;
115
let inst_gen_end = match ft.instructions.get(i + 1) {
116
Some(i) => i.code_offset as usize,
117
None => ft.body_len as usize,
118
};
119
120
if last_wasm_pos > offset {
121
// Start new range.
122
ranges_index.insert(range_wasm_start, ranges.len());
123
ranges.push(Range {
124
wasm_start: range_wasm_start,
125
wasm_end: last_wasm_pos,
126
gen_start: range_gen_start,
127
gen_end: inst_gen_start,
128
positions: current_range.into_boxed_slice(),
129
});
130
range_wasm_start = offset;
131
range_gen_start = inst_gen_start;
132
current_range = Vec::new();
133
last_gen_inst_empty = false;
134
}
135
if last_gen_inst_empty && current_range.last().unwrap().gen_start == inst_gen_start {
136
// It is possible that previous inst_gen_start == inst_gen_end, so
137
// make an attempt to merge all such positions with current one.
138
if inst_gen_start < inst_gen_end {
139
let last = current_range.last_mut().unwrap();
140
last.gen_end = inst_gen_end;
141
last_gen_inst_empty = false;
142
}
143
} else {
144
// Continue existing range: add new wasm->generated code position.
145
current_range.push(Position {
146
wasm_pos: offset,
147
gen_start: inst_gen_start,
148
gen_end: inst_gen_end,
149
});
150
// Track if last position was empty (see if-branch above).
151
last_gen_inst_empty = inst_gen_start == inst_gen_end;
152
}
153
last_wasm_pos = offset;
154
}
155
let last_gen_addr = ft.body_offset + ft.body_len as usize;
156
ranges_index.insert(range_wasm_start, ranges.len());
157
ranges.push(Range {
158
wasm_start: range_wasm_start,
159
wasm_end: fn_end,
160
gen_start: range_gen_start,
161
gen_end: last_gen_addr,
162
positions: current_range.into_boxed_slice(),
163
});
164
165
// Making ranges lookup faster by building index: B-tree with every range
166
// start position that maps into list of active ranges at this position.
167
let ranges = ranges.into_boxed_slice();
168
let mut active_ranges = Vec::new();
169
let mut index = BTreeMap::new();
170
let mut last_wasm_pos = None;
171
for (wasm_start, range_index) in ranges_index {
172
if Some(wasm_start) == last_wasm_pos {
173
active_ranges.push(range_index);
174
continue;
175
}
176
if let Some(position) = last_wasm_pos {
177
let mut sorted_ranges = active_ranges.clone();
178
sorted_ranges.sort();
179
index.insert(position, sorted_ranges.into_boxed_slice());
180
}
181
active_ranges.retain(|r| ranges[*r].wasm_end.cmp(&wasm_start) != std::cmp::Ordering::Less);
182
active_ranges.push(range_index);
183
last_wasm_pos = Some(wasm_start);
184
}
185
active_ranges.sort();
186
index.insert(last_wasm_pos.unwrap(), active_ranges.into_boxed_slice());
187
let index = Vec::from_iter(index);
188
(fn_start, fn_end, FuncLookup { index, ranges })
189
}
190
191
fn build_function_addr_map(
192
compilation: &Compilation<'_>,
193
module: StaticModuleIndex,
194
) -> PrimaryMap<DefinedFuncIndex, FunctionMap> {
195
let mut map = PrimaryMap::new();
196
for idx in compilation.translations[module]
197
.module
198
.defined_func_indices()
199
{
200
let (symbol, metadata) = compilation.function(module, idx);
201
let code_section_offset = compilation.translations[module]
202
.debuginfo
203
.wasm_file
204
.code_section_offset;
205
let ft = &metadata.address_map;
206
let mut fn_map = Vec::new();
207
for t in ft.instructions.iter() {
208
if t.srcloc.file_offset().is_none() {
209
continue;
210
}
211
let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
212
fn_map.push(AddressMap {
213
generated: t.code_offset as usize,
214
wasm: offset,
215
});
216
}
217
218
if cfg!(debug_assertions) {
219
// fn_map is sorted by the generated field -- see FunctionAddressMap::instructions.
220
for i in 1..fn_map.len() {
221
assert!(fn_map[i - 1].generated <= fn_map[i].generated);
222
}
223
}
224
225
map.push(FunctionMap {
226
symbol,
227
offset: ft.body_offset,
228
len: ft.body_len as usize,
229
wasm_start: get_wasm_code_offset(ft.start_srcloc, code_section_offset),
230
wasm_end: get_wasm_code_offset(ft.end_srcloc, code_section_offset),
231
addresses: fn_map.into_boxed_slice(),
232
});
233
}
234
map
235
}
236
237
// Utility iterator to find all ranges starts for specific Wasm address.
238
// The iterator returns generated addresses sorted by RangeIndex.
239
struct TransformRangeStartIter<'a> {
240
addr: WasmAddress,
241
indices: &'a [RangeIndex],
242
ranges: &'a [Range],
243
}
244
245
impl<'a> TransformRangeStartIter<'a> {
246
fn new(func: &'a FuncTransform, addr: WasmAddress) -> Self {
247
let found = match func
248
.lookup
249
.index
250
.binary_search_by(|entry| entry.0.cmp(&addr))
251
{
252
Ok(i) => Some(&func.lookup.index[i].1),
253
Err(i) => {
254
if i > 0 {
255
Some(&func.lookup.index[i - 1].1)
256
} else {
257
None
258
}
259
}
260
};
261
if let Some(range_indices) = found {
262
TransformRangeStartIter {
263
addr,
264
indices: range_indices,
265
ranges: &func.lookup.ranges,
266
}
267
} else {
268
unreachable!();
269
}
270
}
271
}
272
273
impl<'a> Iterator for TransformRangeStartIter<'a> {
274
type Item = (GeneratedAddress, RangeIndex);
275
fn next(&mut self) -> Option<Self::Item> {
276
if let Some((first, tail)) = self.indices.split_first() {
277
let range_index = *first;
278
let range = &self.ranges[range_index];
279
self.indices = tail;
280
let address = match range
281
.positions
282
.binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
283
{
284
Ok(i) => range.positions[i].gen_start,
285
Err(i) => {
286
if i == 0 {
287
range.gen_start
288
} else {
289
range.positions[i - 1].gen_end
290
}
291
}
292
};
293
Some((address, range_index))
294
} else {
295
None
296
}
297
}
298
}
299
300
// Utility iterator to find all ranges ends for specific Wasm address.
301
// The iterator returns generated addresses sorted by RangeIndex.
302
struct TransformRangeEndIter<'a> {
303
addr: WasmAddress,
304
indices: &'a [RangeIndex],
305
ranges: &'a [Range],
306
}
307
308
impl<'a> TransformRangeEndIter<'a> {
309
fn new(func: &'a FuncTransform, addr: WasmAddress) -> Self {
310
let found = match func
311
.lookup
312
.index
313
.binary_search_by(|entry| entry.0.cmp(&addr))
314
{
315
Ok(i) => Some(&func.lookup.index[i].1),
316
Err(i) => {
317
if i > 0 {
318
Some(&func.lookup.index[i - 1].1)
319
} else {
320
None
321
}
322
}
323
};
324
if let Some(range_indices) = found {
325
TransformRangeEndIter {
326
addr,
327
indices: range_indices,
328
ranges: &func.lookup.ranges,
329
}
330
} else {
331
unreachable!();
332
}
333
}
334
}
335
336
impl<'a> Iterator for TransformRangeEndIter<'a> {
337
type Item = (GeneratedAddress, RangeIndex);
338
fn next(&mut self) -> Option<Self::Item> {
339
while let Some((first, tail)) = self.indices.split_first() {
340
let range_index = *first;
341
let range = &self.ranges[range_index];
342
self.indices = tail;
343
if range.wasm_start >= self.addr {
344
continue;
345
}
346
let address = match range
347
.positions
348
.binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
349
{
350
Ok(i) => range.positions[i].gen_end,
351
Err(i) => {
352
if i == range.positions.len() {
353
range.gen_end
354
} else {
355
range.positions[i].gen_start
356
}
357
}
358
};
359
return Some((address, range_index));
360
}
361
None
362
}
363
}
364
365
// Utility iterator to iterate by translated function ranges.
366
struct TransformRangeIter<'a> {
367
func: &'a FuncTransform,
368
start_it: TransformRangeStartIter<'a>,
369
end_it: TransformRangeEndIter<'a>,
370
last_start: Option<(GeneratedAddress, RangeIndex)>,
371
last_end: Option<(GeneratedAddress, RangeIndex)>,
372
last_item: Option<(GeneratedAddress, GeneratedAddress)>,
373
}
374
375
impl<'a> TransformRangeIter<'a> {
376
fn new(func: &'a FuncTransform, start: WasmAddress, end: WasmAddress) -> Self {
377
let mut start_it = TransformRangeStartIter::new(func, start);
378
let last_start = start_it.next();
379
let mut end_it = TransformRangeEndIter::new(func, end);
380
let last_end = end_it.next();
381
TransformRangeIter {
382
func,
383
start_it,
384
end_it,
385
last_start,
386
last_end,
387
last_item: None,
388
}
389
}
390
}
391
392
impl<'a> Iterator for TransformRangeIter<'a> {
393
type Item = (GeneratedAddress, GeneratedAddress);
394
fn next(&mut self) -> Option<Self::Item> {
395
loop {
396
// Merge TransformRangeStartIter and TransformRangeEndIter data using
397
// FuncLookup index's field property to be sorted by RangeIndex.
398
let (start, end, range_index): (
399
Option<GeneratedAddress>,
400
Option<GeneratedAddress>,
401
RangeIndex,
402
) = {
403
match (self.last_start.as_ref(), self.last_end.as_ref()) {
404
(Some((s, sri)), Some((e, eri))) => {
405
if sri == eri {
406
// Start and end RangeIndex matched.
407
(Some(*s), Some(*e), *sri)
408
} else if sri < eri {
409
(Some(*s), None, *sri)
410
} else {
411
(None, Some(*e), *eri)
412
}
413
}
414
(Some((s, sri)), None) => (Some(*s), None, *sri),
415
(None, Some((e, eri))) => (None, Some(*e), *eri),
416
(None, None) => {
417
// Reached ends for start and end iterators.
418
return None;
419
}
420
}
421
};
422
let range_start = match start {
423
Some(range_start) => {
424
// Consume start iterator.
425
self.last_start = self.start_it.next();
426
range_start
427
}
428
None => {
429
let range = &self.func.lookup.ranges[range_index];
430
range.gen_start
431
}
432
};
433
let range_end = match end {
434
Some(range_end) => {
435
// Consume end iterator.
436
self.last_end = self.end_it.next();
437
range_end
438
}
439
None => {
440
let range = &self.func.lookup.ranges[range_index];
441
range.gen_end
442
}
443
};
444
445
if cfg!(debug_assertions) {
446
match self.last_item.replace((range_start, range_end)) {
447
Some((_, last_end)) => debug_assert!(last_end <= range_start),
448
None => (),
449
}
450
}
451
452
if range_start < range_end {
453
return Some((range_start, range_end));
454
}
455
// Throw away empty ranges.
456
debug_assert!(range_start == range_end);
457
}
458
}
459
}
460
461
impl AddressTransform {
462
pub fn new(compilation: &Compilation<'_>, module: StaticModuleIndex) -> Self {
463
let mut func = BTreeMap::new();
464
let code_section_offset = compilation.translations[module]
465
.debuginfo
466
.wasm_file
467
.code_section_offset;
468
469
for idx in compilation.translations[module]
470
.module
471
.defined_func_indices()
472
{
473
let (_, metadata) = compilation.function(module, idx);
474
let (fn_start, fn_end, lookup) =
475
build_function_lookup(&metadata.address_map, code_section_offset);
476
477
func.insert(
478
fn_start,
479
FuncTransform {
480
start: fn_start,
481
end: fn_end,
482
index: idx,
483
lookup,
484
},
485
);
486
}
487
488
let map = build_function_addr_map(compilation, module);
489
let func = Vec::from_iter(func);
490
AddressTransform { map, func }
491
}
492
493
#[cfg(test)]
494
pub fn mock(
495
module_map: &wasmtime_environ::PrimaryMap<
496
wasmtime_environ::DefinedFuncIndex,
497
&crate::CompiledFunctionMetadata,
498
>,
499
wasm_file: wasmtime_environ::WasmFileInfo,
500
) -> Self {
501
use cranelift_entity::EntityRef;
502
503
let mut translations = wasmtime_environ::PrimaryMap::new();
504
let mut translation = wasmtime_environ::ModuleTranslation::new(StaticModuleIndex::new(0));
505
translation.debuginfo.wasm_file = wasm_file;
506
translation
507
.module
508
.push_function(wasmtime_environ::ModuleInternedTypeIndex::from_u32(0));
509
translations.push(translation);
510
511
let mut dummy_obj = object::write::Object::new(
512
object::BinaryFormat::Elf,
513
object::Architecture::Wasm32,
514
object::Endianness::Little,
515
);
516
let dummy_symbol = dummy_obj.add_file_symbol(Vec::new());
517
let func_lookup = move |_, f| (dummy_symbol, module_map[f]);
518
let tunables = wasmtime_environ::Tunables::default_host();
519
let compile = Compilation::new(
520
&*cranelift_codegen::isa::lookup(target_lexicon::Triple::host())
521
.unwrap()
522
.finish(cranelift_codegen::settings::Flags::new(
523
cranelift_codegen::settings::builder(),
524
))
525
.unwrap(),
526
&translations,
527
&func_lookup,
528
None,
529
&tunables,
530
);
531
Self::new(&compile, StaticModuleIndex::from_u32(0))
532
}
533
534
fn find_func(&self, addr: u64) -> Option<&FuncTransform> {
535
// TODO check if we need to include end address
536
let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) {
537
Ok(i) => &self.func[i].1,
538
Err(i) => {
539
if i > 0 {
540
&self.func[i - 1].1
541
} else {
542
return None;
543
}
544
}
545
};
546
if addr >= func.start {
547
return Some(func);
548
}
549
None
550
}
551
552
pub fn find_func_index(&self, addr: u64) -> Option<DefinedFuncIndex> {
553
self.find_func(addr).map(|f| f.index)
554
}
555
556
fn translate_raw(&self, addr: u64) -> Option<(usize, GeneratedAddress)> {
557
const TOMBSTONE: u64 = u32::MAX as u64;
558
if addr == 0 || addr == TOMBSTONE {
559
// Addresses for unlinked code may be left as 0 or replaced
560
// with -1, depending on the linker used.
561
return None;
562
}
563
if let Some(func) = self.find_func(addr) {
564
let map = &self.map[func.index];
565
if addr == func.end {
566
// Clamp last address to the end to extend translation to the end
567
// of the function.
568
return Some((map.symbol, map.len));
569
}
570
let first_result = TransformRangeStartIter::new(func, addr).next();
571
first_result.map(|(address, _)| (map.symbol, address))
572
} else {
573
// Address was not found: function was not compiled?
574
None
575
}
576
}
577
578
pub fn can_translate_address(&self, addr: u64) -> bool {
579
self.translate(addr).is_some()
580
}
581
582
pub fn translate(&self, addr: u64) -> Option<write::Address> {
583
self.translate_raw(addr)
584
.map(|(symbol, address)| write::Address::Symbol {
585
symbol,
586
addend: address as i64,
587
})
588
}
589
590
pub fn translate_ranges_raw<'a>(
591
&'a self,
592
start: u64,
593
end: u64,
594
) -> Option<(usize, impl Iterator<Item = (usize, usize)> + 'a)> {
595
if start == 0 {
596
// It's normally 0 for debug info without the linked code.
597
return None;
598
}
599
if let Some(func) = self.find_func(start) {
600
let result = TransformRangeIter::new(func, start, end);
601
let symbol = self.map[func.index].symbol;
602
return Some((symbol, result));
603
}
604
// Address was not found: function was not compiled?
605
None
606
}
607
608
pub fn translate_ranges<'a>(
609
&'a self,
610
start: u64,
611
end: u64,
612
) -> impl Iterator<Item = (write::Address, u64)> + 'a {
613
enum TranslateRangesResult<'a> {
614
Empty,
615
Raw {
616
symbol: usize,
617
it: Box<dyn Iterator<Item = (usize, usize)> + 'a>,
618
},
619
}
620
impl<'a> Iterator for TranslateRangesResult<'a> {
621
type Item = (write::Address, u64);
622
fn next(&mut self) -> Option<Self::Item> {
623
match self {
624
TranslateRangesResult::Empty => None,
625
TranslateRangesResult::Raw { symbol, it } => match it.next() {
626
Some((start, end)) => {
627
debug_assert!(start < end);
628
Some((
629
write::Address::Symbol {
630
symbol: *symbol,
631
addend: start as i64,
632
},
633
(end - start) as u64,
634
))
635
}
636
None => None,
637
},
638
}
639
}
640
}
641
642
match self.translate_ranges_raw(start, end) {
643
Some((symbol, ranges)) => TranslateRangesResult::Raw {
644
symbol,
645
it: Box::new(ranges),
646
},
647
None => TranslateRangesResult::Empty,
648
}
649
}
650
651
pub fn map(&self) -> &PrimaryMap<DefinedFuncIndex, FunctionMap> {
652
&self.map
653
}
654
655
pub fn func_range(&self, index: DefinedFuncIndex) -> (GeneratedAddress, GeneratedAddress) {
656
let map = &self.map[index];
657
(map.offset, map.offset + map.len)
658
}
659
660
pub fn func_source_range(&self, index: DefinedFuncIndex) -> (WasmAddress, WasmAddress) {
661
let map = &self.map[index];
662
(map.wasm_start, map.wasm_end)
663
}
664
}
665
666
#[cfg(test)]
667
mod tests {
668
use super::{AddressTransform, build_function_lookup, get_wasm_code_offset};
669
use crate::{CompiledFunctionMetadata, FunctionAddressMap};
670
use cranelift_entity::PrimaryMap;
671
use gimli::write::Address;
672
use std::mem;
673
use wasmtime_environ::{FilePos, InstructionAddressMap, WasmFileInfo};
674
675
#[test]
676
fn test_get_wasm_code_offset() {
677
let offset = get_wasm_code_offset(FilePos::new(3), 1);
678
assert_eq!(2, offset);
679
let offset = get_wasm_code_offset(FilePos::new(16), 0xF000_0000);
680
assert_eq!(0x1000_0010, offset);
681
let offset = get_wasm_code_offset(FilePos::new(1), 0x20_8000_0000);
682
assert_eq!(0x8000_0001, offset);
683
}
684
685
fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap {
686
FunctionAddressMap {
687
instructions: vec![
688
InstructionAddressMap {
689
srcloc: FilePos::new(wasm_offset + 2),
690
code_offset: 5,
691
},
692
InstructionAddressMap {
693
srcloc: FilePos::default(),
694
code_offset: 8,
695
},
696
InstructionAddressMap {
697
srcloc: FilePos::new(wasm_offset + 7),
698
code_offset: 15,
699
},
700
InstructionAddressMap {
701
srcloc: FilePos::default(),
702
code_offset: 23,
703
},
704
]
705
.into(),
706
start_srcloc: FilePos::new(wasm_offset),
707
end_srcloc: FilePos::new(wasm_offset + 10),
708
body_offset: 0,
709
body_len: 30,
710
}
711
}
712
713
#[test]
714
fn test_build_function_lookup_simple() {
715
let input = create_simple_func(11);
716
let (start, end, lookup) = build_function_lookup(&input, 1);
717
assert_eq!(10, start);
718
assert_eq!(20, end);
719
720
assert_eq!(1, lookup.index.len());
721
let index_entry = lookup.index.into_iter().next().unwrap();
722
assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry);
723
assert_eq!(1, lookup.ranges.len());
724
let range = &lookup.ranges[0];
725
assert_eq!(10, range.wasm_start);
726
assert_eq!(20, range.wasm_end);
727
assert_eq!(0, range.gen_start);
728
assert_eq!(30, range.gen_end);
729
let positions = &range.positions;
730
assert_eq!(2, positions.len());
731
assert_eq!(12, positions[0].wasm_pos);
732
assert_eq!(5, positions[0].gen_start);
733
assert_eq!(8, positions[0].gen_end);
734
assert_eq!(17, positions[1].wasm_pos);
735
assert_eq!(15, positions[1].gen_start);
736
assert_eq!(23, positions[1].gen_end);
737
}
738
739
#[test]
740
fn test_build_function_lookup_two_ranges() {
741
let mut input = create_simple_func(11);
742
// append instruction with same srcloc as input.instructions[0]
743
let mut list = Vec::from(mem::take(&mut input.instructions));
744
list.push(InstructionAddressMap {
745
srcloc: FilePos::new(11 + 2),
746
code_offset: 23,
747
});
748
list.push(InstructionAddressMap {
749
srcloc: FilePos::default(),
750
code_offset: 26,
751
});
752
input.instructions = list.into();
753
let (start, end, lookup) = build_function_lookup(&input, 1);
754
assert_eq!(10, start);
755
assert_eq!(20, end);
756
757
assert_eq!(2, lookup.index.len());
758
let index_entries = Vec::from_iter(lookup.index);
759
assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]);
760
assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]);
761
assert_eq!(2, lookup.ranges.len());
762
763
let range = &lookup.ranges[0];
764
assert_eq!(10, range.wasm_start);
765
assert_eq!(17, range.wasm_end);
766
assert_eq!(0, range.gen_start);
767
assert_eq!(23, range.gen_end);
768
let positions = &range.positions;
769
assert_eq!(2, positions.len());
770
assert_eq!(12, positions[0].wasm_pos);
771
assert_eq!(5, positions[0].gen_start);
772
assert_eq!(8, positions[0].gen_end);
773
assert_eq!(17, positions[1].wasm_pos);
774
assert_eq!(15, positions[1].gen_start);
775
assert_eq!(23, positions[1].gen_end);
776
777
let range = &lookup.ranges[1];
778
assert_eq!(12, range.wasm_start);
779
assert_eq!(20, range.wasm_end);
780
assert_eq!(23, range.gen_start);
781
assert_eq!(30, range.gen_end);
782
let positions = &range.positions;
783
assert_eq!(1, positions.len());
784
assert_eq!(12, positions[0].wasm_pos);
785
assert_eq!(23, positions[0].gen_start);
786
assert_eq!(26, positions[0].gen_end);
787
}
788
789
#[test]
790
fn test_addr_translate() {
791
// Ignore this test if cranelift doesn't support the native platform.
792
if cranelift_native::builder().is_err() {
793
return;
794
}
795
let func = CompiledFunctionMetadata {
796
address_map: create_simple_func(11),
797
..Default::default()
798
};
799
let input = PrimaryMap::from_iter([&func]);
800
let at = AddressTransform::mock(
801
&input,
802
WasmFileInfo {
803
path: None,
804
code_section_offset: 1,
805
imported_func_count: 0,
806
funcs: Vec::new(),
807
},
808
);
809
810
let addr = at.translate(10);
811
assert_eq!(
812
Some(Address::Symbol {
813
symbol: 0,
814
addend: 0,
815
}),
816
addr
817
);
818
819
let addr = at.translate(20);
820
assert_eq!(
821
Some(Address::Symbol {
822
symbol: 0,
823
addend: 30,
824
}),
825
addr
826
);
827
828
let addr = at.translate(0);
829
assert_eq!(None, addr);
830
831
let addr = at.translate(12);
832
assert_eq!(
833
Some(Address::Symbol {
834
symbol: 0,
835
addend: 5,
836
}),
837
addr
838
);
839
840
let addr = at.translate(18);
841
assert_eq!(
842
Some(Address::Symbol {
843
symbol: 0,
844
addend: 23,
845
}),
846
addr
847
);
848
}
849
}
850
851