From e81a0904b9e1a45844179b8e67016c2bb27c7760 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 25 Aug 2021 11:04:28 -0700 Subject: [PATCH 01/17] Use relative `call` instructions between wasm functions This commit is a relatively major change to the way that Wasmtime generates code for Wasm modules and how functions call each other. Prior to this commit all function calls between functions, even if they were defined in the same module, were done indirectly through a register. To implement this the backend would emit an absolute 8-byte relocation near all function calls, load that address into a register, and then call it. While this technique is simple to implement and easy to get right, it has two primary downsides associated with it: * Function calls are always indirect which means they are more difficult to predict, resulting in worse performance. * Generating a relocation-per-function call requires expensive relocation resolution at module-load time, which can be a large contributing factor to how long it takes to load a precompiled module. To fix these issues, while also compromising on the previously simple implementation technique, this commit switches wasm calls within a module to using the `colocated` flag enabled in Cranelift-speak, which basically means that a relative call instruction is used with a relocation that's resolved relative to the pc of the call instruction itself. When switching the `colocated` flag to `true` this commit is also then able to move much of the relocation resolution from `wasmtime_jit::link` into `wasmtime_cranelift::obj` during object-construction time. This frontloads all relocation work which means that there's actually no relocations related to function calls in the final image, solving both of our points above. The main gotcha in implementing this technique is that there are hardware limitations to relative function calls which mean we can't simply blindly use them. AArch64, for example, can only go +/- 64 MB from the `bl` instruction to the target, which means that if the function we're calling is a greater distance away then we would fail to resolve that relocation. On x86_64 the limits are +/- 2GB which are much larger, but theoretically still feasible to hit. Consequently the main increase in implementation complexity is fixing this issue. This issue is actually already present in Cranelift itself, and is internally one of the invariants handled by the `MachBuffer` type. When generating a function relative jumps between basic blocks have similar restrictions. At this time, however, I decided to not try to reuse `MachBuffer` for inter-function relocations. The main reason for this is that `MachBuffer` doesn't actually handle any of the cases that inter-function relocations need to handle, namely the 26-bit relocation and 32-bit relocations of AArch64 and x86_64. If a 26-bit relocation isn't resolvable because a function gets too large then `MachBuffer` will simply panic. I also opted to not use `MachBuffer` for now, though, because it doesn't quite have the infrastructure already set up where when inserting an island for a smaller jump sequence the result should be a relocation to fill in later. Today it simply promotes a 19-bit branch on AArch64 to a 26-bit branch, assuming the 26 bits is enough. All-in-all I felt that at this time `MachBuffer` wasn't reusable enough and would need enough work that it was probably more worthwhile to do this in `wasmtime-cranelift` first and looking to unify the strategies in the future. For these reasons the `wasmtime_cranelift::obj` module has grown in complexity quite a bit. This now entirely handles relative relocations between functions, automatically inserting "jump veneers" to resolve calls between functions that are too far away. The general strategy is similar to `MachBuffer`, but different in the final tracking of relocations. The current assumption is that each `TargetIsa` has the ability to generate a fixed "jump veneer" which internally has an 8-byte immediate value that is added to its own address to reach the destination of an indirect call. This relative jump, even in the veneer, means that when veneers are used we still don't need absolute relocations since the code is still all implemented as relative jumps. The veneer jumps, however, are larger in code size because they don't fit into the native versions. I've added some simple testing of this for now. A synthetic compiler option was create to simply add padded 0s between functions and test cases implement various forms of calls that at least need veneers. A test is also included for x86_64, but it is unfortunately pretty slow because it requires generating 2GB of output. I'm hoping for now it's not too bad, but we can disable the test if it's prohibitive and otherwise just comment the necessary portions to be sure to run the ignored test if these parts of the code have changed. The final end-result of this commit is that for a large module I'm working with the number of relocations dropped to zero, meaning that nothing actually needs to be done to the text section when it's loaded into memory (yay!). I haven't run final benchmarks yet but this is the last remaining source of significant slowdown when loading modules, after I land a number of other PRs both active and ones that I only have locally for now. --- .../codegen/src/isa/aarch64/inst/emit.rs | 28 + cranelift/codegen/src/isa/aarch64/mod.rs | 17 + cranelift/codegen/src/isa/s390x/mod.rs | 11 + cranelift/codegen/src/isa/x64/inst/emit.rs | 55 +- cranelift/codegen/src/isa/x64/inst/mod.rs | 2 + cranelift/codegen/src/isa/x64/mod.rs | 8 + cranelift/codegen/src/machinst/mod.rs | 28 + crates/bench-api/foo.o | Bin 0 -> 9672 bytes crates/cranelift/src/builder.rs | 17 +- crates/cranelift/src/compiler.rs | 112 ++-- crates/cranelift/src/func_environ.rs | 19 +- crates/cranelift/src/lib.rs | 5 +- crates/cranelift/src/obj.rs | 618 +++++++++++++++--- crates/fuzzing/foo.o | Bin 0 -> 10000 bytes crates/jit/src/instantiate.rs | 1 + crates/jit/src/link.rs | 94 +-- crates/test-programs/foo.o | Bin 0 -> 169344 bytes crates/wasmtime/foo.o | Bin 0 -> 9760 bytes crates/wiggle/foo.o | Bin 0 -> 9840 bytes tests/all/main.rs | 1 + tests/all/relocs.rs | 119 ++++ 21 files changed, 894 insertions(+), 241 deletions(-) create mode 100644 crates/bench-api/foo.o create mode 100644 crates/fuzzing/foo.o create mode 100644 crates/test-programs/foo.o create mode 100644 crates/wasmtime/foo.o create mode 100644 crates/wiggle/foo.o create mode 100644 tests/all/relocs.rs diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index ce669459e1ca..0e7f93efc708 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -19,6 +19,34 @@ pub fn memlabel_finalize(_insn_off: CodeOffset, label: &MemLabel) -> i32 { } } +/// Generates the four instructions necessary for a small "jump veneer" which +/// is used when relative 26-bit call instructions won't cut it and a longer +/// jump is needed. +/// +/// This generats: +/// +/// adr x16, 16 +/// ldur x16, [x17] +/// add x16, x16, x17 +/// br x16 +/// +/// and the expectation is that the 8-byte immediate address to jump to is +/// located after these instructions are encoded. +/// +/// Note that this is part of the `MachBackend::gen_jump_veneer` contract. +pub fn gen_jump_veneer() -> (u32, u32, u32, u32) { + ( + // adr x17, 16 + enc_adr(16, writable_xreg(17)), + // ldr x16, [x17] + enc_ldst_simm9(0b1111100001, SImm9::zero(), 0b00, xreg(17), xreg(16)), + // add x16, x16, x17 + enc_arith_rrr(0b10001011_000, 0, writable_xreg(16), xreg(16), xreg(17)), + // br x16 + enc_br(xreg(16)), + ) +} + /// Memory addressing mode finalization: convert "special" modes (e.g., /// generic arbitrary stack offset) into real addressing modes, possibly by /// emitting some helper instructions that come immediately before the use diff --git a/cranelift/codegen/src/isa/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs index 3243d0f7b462..d471e3e75c8f 100644 --- a/cranelift/codegen/src/isa/aarch64/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/mod.rs @@ -161,6 +161,23 @@ impl MachBackend for AArch64Backend { fn create_systemv_cie(&self) -> Option { Some(inst::unwind::systemv::create_cie()) } + + fn max_jump_veneer_size(&self) -> usize { + 24 // 4 insns + 8-byte immediate + } + + fn generate_jump_veneer(&self) -> (Vec, usize) { + let (a, b, c, d) = inst::emit::gen_jump_veneer(); + let mut bytes = Vec::with_capacity(self.max_jump_veneer_size()); + bytes.extend_from_slice(&a.to_le_bytes()); + bytes.extend_from_slice(&b.to_le_bytes()); + bytes.extend_from_slice(&c.to_le_bytes()); + bytes.extend_from_slice(&d.to_le_bytes()); + let imm_start = bytes.len(); + bytes.extend_from_slice(&[0x00; 8]); + assert_eq!(bytes.len(), self.max_jump_veneer_size()); + (bytes, imm_start) + } } /// Create a new `isa::Builder`. diff --git a/cranelift/codegen/src/isa/s390x/mod.rs b/cranelift/codegen/src/isa/s390x/mod.rs index d83791cb9818..74bfe3924ea8 100644 --- a/cranelift/codegen/src/isa/s390x/mod.rs +++ b/cranelift/codegen/src/isa/s390x/mod.rs @@ -165,6 +165,17 @@ impl MachBackend for S390xBackend { fn map_reg_to_dwarf(&self, reg: Reg) -> Result { inst::unwind::systemv::map_reg(reg).map(|reg| reg.0) } + + fn max_jump_veneer_size(&self) -> usize { + 0 + } + + fn generate_jump_veneer(&self) -> (Vec, usize) { + panic!( + "jumps >= 2gb are not implemented yet on s390x, functions are \ + too far apart to have a relative call between them" + ); + } } /// Create a new `isa::Builder`. diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 93132a2aabe0..b9a9f8b35b74 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -4,9 +4,9 @@ use crate::ir::LibCall; use crate::ir::TrapCode; use crate::isa::x64::encoding::evex::{EvexInstruction, EvexVectorLength}; use crate::isa::x64::encoding::rex::{ - emit_simm, emit_std_enc_enc, emit_std_enc_mem, emit_std_reg_mem, emit_std_reg_reg, int_reg_enc, - low8_will_sign_extend_to_32, low8_will_sign_extend_to_64, reg_enc, LegacyPrefixes, OpcodeMap, - RexFlags, + emit_simm, emit_std_enc_enc, emit_std_enc_mem, emit_std_reg_mem, emit_std_reg_reg, + encode_modrm, int_reg_enc, low8_will_sign_extend_to_32, low8_will_sign_extend_to_64, reg_enc, + LegacyPrefixes, OpcodeMap, RexFlags, }; use crate::isa::x64::inst::args::*; use crate::isa::x64::inst::*; @@ -56,6 +56,55 @@ fn emit_reloc( sink.add_reloc(srcloc, kind, name, addend); } +/// Generates the instructions necessary for a small "jump veneer" which is +/// used when relative 32-bit call instructions won't cut it and a longer jump +/// is needed. +/// +/// This generats: +/// +/// movabsq $val, %r10 +/// lea -15(%rip), %r11 +/// add %r10, %r11 +/// jmpq *%r11 +/// +/// Note that this is part of the `MachBackend::gen_jump_veneer` contract. +pub fn gen_jump_veneer() -> (Vec, usize) { + let mut bytes = Vec::with_capacity(jump_veneer_size()); + + let r10 = int_reg_enc(regs::r10()); + let r11 = int_reg_enc(regs::r11()); + + // movabsq $val, %r10 + bytes.push(0x48 | ((r10 >> 3) & 1)); + bytes.push(0xB8 | (r10 & 7)); + let imm_pos = bytes.len(); + bytes.extend_from_slice(&[0; 8]); + + // lea -15(%rip), %r11 + bytes.push(0x48 | ((r11 >> 3) & 1) << 2); + bytes.push(0x8d); + bytes.push(encode_modrm(0b00, r11, 0b101)); + bytes.extend_from_slice(&i32::to_le_bytes(-15)); + + // add %r10, %r11 + bytes.push(0x48 | (((r11 >> 3) & 1) << 2) | ((r10 >> 3) & 1)); + bytes.push(0x01); + bytes.push(encode_modrm(0b11, r10, r11)); + + // jmpq *%r11 + bytes.push(0x40 | ((r11 >> 3) & 1)); + bytes.push(0xff); + bytes.push(0xe0 | (r11 & 7)); + + assert_eq!(bytes.len(), jump_veneer_size()); + (bytes, imm_pos) +} + +/// See `gen_jump_veneer`. +pub fn jump_veneer_size() -> usize { + 23 +} + /// The top-level emit function. /// /// Important! Do not add improved (shortened) encoding cases to existing diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index cb5b27dfbc86..3ba4c7e6a343 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -28,6 +28,8 @@ pub mod unwind; use args::*; use regs::{create_reg_universe_systemv, show_ireg_sized}; +pub use emit::{gen_jump_veneer, jump_veneer_size}; + //============================================================================= // Instructions (top level): definition diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index 381898e485ea..820ba829c702 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -158,6 +158,14 @@ impl MachBackend for X64Backend { fn map_reg_to_dwarf(&self, reg: Reg) -> Result { inst::unwind::systemv::map_reg(reg).map(|reg| reg.0) } + + fn max_jump_veneer_size(&self) -> usize { + inst::jump_veneer_size() + } + + fn generate_jump_veneer(&self) -> (Vec, usize) { + inst::gen_jump_veneer() + } } /// Create a new `isa::Builder`. diff --git a/cranelift/codegen/src/machinst/mod.rs b/cranelift/codegen/src/machinst/mod.rs index 41d6e05bedf9..aca4917b2835 100644 --- a/cranelift/codegen/src/machinst/mod.rs +++ b/cranelift/codegen/src/machinst/mod.rs @@ -425,6 +425,34 @@ pub trait MachBackend { fn map_reg_to_dwarf(&self, _: Reg) -> Result { Err(RegisterMappingError::UnsupportedArchitecture) } + + /// Generates as "veneer" which is used when a relative call instruction + /// cannot reach to the destination. + /// + /// Cranelift compiles wasm modules on a per-function basis entirely + /// isolated from all other functions. Functions also, ideally, use relative + /// calls between them to avoid needing relocation fixups when a module is + /// loaded and also having statically more predictable calls. These jumps, + /// however, may not always be able to reach the destination depending on + /// the final layout of the executable. + /// + /// This function is used to generate an executable code sequence which can + /// be used to jump to an arbitrary pointer-sized immediate. This is + /// only used when functions are too far apart to call each other with + /// relative call instructions. + /// + /// The first return of this function is the machine code of the veneer, and + /// the second argument is the offset, within the veneer, where an 8-byte + /// immediate needs to be written of the target destination. The veneer, + /// when jumped to, will add the 8-byte immediate to the address of the + /// 8-byte immediate and jump to that location. This means that the veneer + /// will do a relative jump to the final location, and the relative jump + /// uses a pointer-sized immediate to make the jump. + fn generate_jump_veneer(&self) -> (Vec, usize); + + /// Returns the maximal size of the veneer returned by + /// `generate_jump_veneer`. + fn max_jump_veneer_size(&self) -> usize; } /// Expected unwind info type. diff --git a/crates/bench-api/foo.o b/crates/bench-api/foo.o new file mode 100644 index 0000000000000000000000000000000000000000..230f693dca444a20edc0714bd2b10edaee04c68f GIT binary patch literal 9672 zcmeI2zi-n(6vv!Iz<8@CKltQwqhwxs`%0dIxsRL zCI%2Als|w8@nd9RL7nN$08$n&QF&+kuzks~uvF?h$-ehK{=RpAHW(f8 z(tfWi^dA)bj)D&r{Gx)xmJ7wqp0!{mmi?3&cZB$8~yc$TFFCb$J3rrPuCu zdqtgu;x)roPQ*z#M*2NB$Qs3(0^#q~bp3Xw;V)Gryvh$-c?}ZQ>vx*PvIN=O7~8|A z%`ZfSgz;)cXBOP y4=Vf)5Tyl2FxK&!;Sk5EWCH&G$GJQ<78CM7T2M;i49X_ee?6bn-;^55^?v~F*zj2Z literal 0 HcmV?d00001 diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs index f7f6c1dba236..41c6560ac687 100644 --- a/crates/cranelift/src/builder.rs +++ b/crates/cranelift/src/builder.rs @@ -13,6 +13,11 @@ use wasmtime_environ::{CompilerBuilder, Setting, SettingKind}; struct Builder { flags: settings::Builder, isa_flags: isa::Builder, + + // A debug-only setting used to synthetically insert 0-byte padding between + // compiled functions to simulate huge compiled artifacts and exercise logic + // related to jump veneers. + padding_between_functions: usize, } pub fn builder() -> Box { @@ -32,6 +37,7 @@ pub fn builder() -> Box { Box::new(Builder { flags, isa_flags: cranelift_native::builder().expect("host machine is not a supported target"), + padding_between_functions: 0, }) } @@ -50,6 +56,12 @@ impl CompilerBuilder for Builder { } fn set(&mut self, name: &str, value: &str) -> Result<()> { + // Special wasmtime-cranelift-only setting. + if name == "padding_between_functions" { + self.padding_between_functions = value.parse()?; + return Ok(()); + } + if let Err(err) = self.flags.set(name, value) { match err { SetError::BadName(_) => { @@ -80,7 +92,10 @@ impl CompilerBuilder for Builder { .isa_flags .clone() .finish(settings::Flags::new(self.flags.clone())); - Box::new(crate::compiler::Compiler::new(isa)) + Box::new(crate::compiler::Compiler::new( + isa, + self.padding_between_functions, + )) } fn settings(&self) -> Vec { diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 3d3443e973e5..1053edcc5ad5 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -36,13 +36,15 @@ use wasmtime_environ::{ pub(crate) struct Compiler { translators: Mutex>, isa: Box, + padding_between_functions: usize, } impl Compiler { - pub(crate) fn new(isa: Box) -> Compiler { + pub(crate) fn new(isa: Box, padding_between_functions: usize) -> Compiler { Compiler { translators: Default::default(), isa, + padding_between_functions, } } @@ -170,7 +172,7 @@ impl wasmtime_environ::Compiler for Compiler { self.save_translator(func_translator); let mut code_buf: Vec = Vec::new(); - let mut reloc_sink = RelocSink::new(func_index); + let mut reloc_sink = RelocSink::new(); let mut trap_sink = TrapSink::new(); let mut stack_map_sink = StackMapSink::default(); context @@ -201,35 +203,41 @@ impl wasmtime_environ::Compiler for Compiler { None }; - Ok(Box::new(CompiledFunction { - body: code_buf, - jt_offsets: context.func.jt_offsets, - relocations: reloc_sink.func_relocs, - value_labels_ranges: ranges.unwrap_or(Default::default()), - stack_slots: context.func.stack_slots, - unwind_info, - traps: trap_sink.traps, - info: FunctionInfo { - start_srcloc: address_transform.start_srcloc, - stack_maps: stack_map_sink.finish(), - }, - address_map: address_transform, - })) + Ok(( + code_buf, + Box::new(CompiledFunction { + body: code_buf, + jt_offsets: context.func.jt_offsets, + relocations: reloc_sink.func_relocs, + value_labels_ranges: ranges.unwrap_or(Default::default()), + stack_slots: context.func.stack_slots, + unwind_info, + traps: trap_sink.traps, + info: FunctionInfo { + start_srcloc: address_transform.start_srcloc, + stack_maps: stack_map_sink.finish(), + }, + address_map: address_transform, + }), + )) } fn emit_obj( &self, translation: &ModuleTranslation, types: &TypeTables, - funcs: PrimaryMap>, + raw_funcs: PrimaryMap>, emit_dwarf: bool, obj: &mut Object, ) -> Result> { const CODE_SECTION_ALIGNMENT: u64 = 0x1000; - let funcs: crate::CompiledFunctions = funcs - .into_iter() - .map(|(_i, f)| *f.downcast().unwrap()) - .collect(); + let mut funcs = PrimaryMap::with_capacity(raw_funcs.len()); + let mut bodies = PrimaryMap::with_capacity(raw_funcs.len()); + for (index, func) in raw_funcs { + let (body, func) = *func.downcast().unwrap(); + assert_eq!(index, funcs.push(func)); + assert_eq!(index, bodies.push(body)); + } // Build trampolines for every signature that can be used by this module. let signatures = translation @@ -255,9 +263,12 @@ impl wasmtime_environ::Compiler for Compiler { let range = builder.func(i, func); addrs.push(range.clone(), &func.address_map.instructions); traps.push(range, &func.traps); + if self.padding_between_functions > 0 { + builder.append_synthetic_padding(self.padding_between_functions); + } } - for (i, func) in trampolines.iter() { - builder.trampoline(*i, func); + for (i, (body, func)) in trampolines.iter_mut() { + builder.trampoline(*i, func, mem::take(body)); } builder.align_text_to(CODE_SECTION_ALIGNMENT); @@ -291,9 +302,9 @@ impl wasmtime_environ::Compiler for Compiler { builder.dwarf_sections(&dwarf_sections)?; } - builder.finish(&*self.isa)?; addrs.append_to(obj); traps.append_to(obj); + builder.finish()?; Ok(funcs.into_iter().map(|(_, f)| f.info).collect()) } @@ -304,13 +315,13 @@ impl wasmtime_environ::Compiler for Compiler { host_fn: usize, obj: &mut Object, ) -> Result<()> { - let host_to_wasm = self.host_to_wasm_trampoline(ty)?; - let wasm_to_host = self.wasm_to_host_trampoline(ty, host_fn)?; + let (body1, host_to_wasm) = self.host_to_wasm_trampoline(ty)?; + let (body2, wasm_to_host) = self.wasm_to_host_trampoline(ty, host_fn)?; let module = Module::new(); - let mut builder = ObjectBuilder::new(obj, &module); - builder.trampoline(SignatureIndex::new(0), &host_to_wasm); - builder.trampoline(SignatureIndex::new(1), &wasm_to_host); - builder.finish(&*self.isa)?; + let mut builder = ObjectBuilder::new(obj, &module, &*self.isa); + builder.trampoline(SignatureIndex::new(0), &host_to_wasm, body1); + builder.trampoline(SignatureIndex::new(1), &wasm_to_host, body2); + builder.finish()?; Ok(()) } @@ -345,7 +356,10 @@ fn to_flag_value(v: &settings::Value) -> FlagValue { } impl Compiler { - fn host_to_wasm_trampoline(&self, ty: &WasmFuncType) -> Result { + fn host_to_wasm_trampoline( + &self, + ty: &WasmFuncType, + ) -> Result<(Vec, CompiledFunction), CompileError> { let isa = &*self.isa; let value_size = mem::size_of::(); let pointer_type = isa.pointer_type(); @@ -431,7 +445,7 @@ impl Compiler { &self, ty: &WasmFuncType, host_fn: usize, - ) -> Result { + ) -> Result<(Vec, CompiledFunction), CompileError> { let isa = &*self.isa; let pointer_type = isa.pointer_type(); let wasm_signature = indirect_signature(isa, ty); @@ -506,7 +520,7 @@ impl Compiler { &self, mut context: Context, isa: &dyn TargetIsa, - ) -> Result { + ) -> Result<(Vec, CompiledFunction), CompileError> { let mut code_buf = Vec::new(); let mut reloc_sink = TrampolineRelocSink::default(); let mut trap_sink = binemit::NullTrapSink {}; @@ -527,17 +541,19 @@ impl Compiler { CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) })?; - Ok(CompiledFunction { - body: code_buf, - jt_offsets: context.func.jt_offsets, - unwind_info, - relocations: reloc_sink.relocs, - stack_slots: Default::default(), - value_labels_ranges: Default::default(), - info: Default::default(), - address_map: Default::default(), - traps: Vec::new(), - }) + Ok(( + code_buf, + CompiledFunction { + jt_offsets: context.func.jt_offsets, + unwind_info, + relocations: reloc_sink.relocs, + stack_slots: Default::default(), + value_labels_ranges: Default::default(), + info: Default::default(), + address_map: Default::default(), + traps: Vec::new(), + }, + )) } } @@ -606,9 +622,6 @@ fn collect_address_maps( /// Implementation of a relocation sink that just saves all the information for later struct RelocSink { - /// Current function index. - func_index: FuncIndex, - /// Relocations recorded for the function. func_relocs: Vec, } @@ -651,7 +664,7 @@ impl binemit::RelocSink for RelocSink { fn reloc_jt(&mut self, offset: binemit::CodeOffset, reloc: binemit::Reloc, jt: ir::JumpTable) { self.func_relocs.push(Relocation { reloc, - reloc_target: RelocationTarget::JumpTable(self.func_index, jt), + reloc_target: RelocationTarget::JumpTable(jt), offset, addend: 0, }); @@ -660,9 +673,8 @@ impl binemit::RelocSink for RelocSink { impl RelocSink { /// Return a new `RelocSink` instance. - fn new(func_index: FuncIndex) -> Self { + fn new() -> Self { Self { - func_index, func_relocs: Vec::new(), } } diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index afcb6e8e1ad4..67ddf8d944f5 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -1295,9 +1295,22 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m Ok(func.import_function(ir::ExtFuncData { name, signature, - // We currently allocate all code segments independently, so nothing - // is colocated. - colocated: false, + + // The value of this flag determines the codegen for calls to this + // function. If this flag is `false` then absolute relocations will + // be generated for references to the function, which requires + // load-time relocation resolution. If this flag is set to `true` + // then relative relocations are emitted which can be resolved at + // object-link-time, just after all functions are compiled. + // + // This flag is set to `true` for functions defined in the object + // we'll be defining in this compilation unit, or everything local + // to the wasm module. This means that between functions in a wasm + // module there's relative calls encoded. All calls external to a + // wasm module (e.g. imports or libcalls) are either encoded through + // the `VMContext` as relative jumps (hence no relocations) or + // they're libcalls with absolute relocations. + colocated: self.module.defined_func_index(index).is_some(), })) } diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs index 3980c9864001..a365fa36678e 100644 --- a/crates/cranelift/src/lib.rs +++ b/crates/cranelift/src/lib.rs @@ -111,9 +111,6 @@ type CompiledFunctions = PrimaryMap; /// Compiled function: machine code body, jump table offsets, and unwind information. #[derive(Default)] pub struct CompiledFunction { - /// The machine code for this function. - body: Vec, - /// The jump tables offsets (in the body). jt_offsets: ir::JumpTableOffsets, @@ -182,7 +179,7 @@ enum RelocationTarget { /// A compiler-generated libcall. LibCall(ir::LibCall), /// Jump table index. - JumpTable(FuncIndex, ir::JumpTable), + JumpTable(ir::JumpTable), } /// Creates a new cranelift `Signature` with no wasm params/results for the diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index ac6d81dcfad9..e5e0e6a956e3 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -17,7 +17,7 @@ use crate::debug::{DwarfSection, DwarfSectionRelocTarget}; use crate::{CompiledFunction, Relocation, RelocationTarget}; use anyhow::Result; use cranelift_codegen::binemit::Reloc; -use cranelift_codegen::ir::{JumpTableOffsets, LibCall}; +use cranelift_codegen::ir::LibCall; use cranelift_codegen::isa::{ unwind::{systemv, UnwindInfo}, TargetIsa, @@ -29,11 +29,12 @@ use object::write::{ SymbolSection, }; use object::{ - elf, Architecture, RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, + Architecture, RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; use std::collections::HashMap; use std::convert::TryFrom; +use std::mem; use std::ops::Range; use wasmtime_environ::obj; use wasmtime_environ::{ @@ -91,16 +92,111 @@ fn write_libcall_symbols(obj: &mut Object) -> HashMap { libcalls } +/// A helper structure used to assemble the final text section of an exectuable, +/// plus unwinding information and other related details. +/// +/// This builder relies on Cranelift-specific internals but assembles into a +/// generic `Object` which will get further appended to in a compiler-agnostic +/// fashion later. pub struct ObjectBuilder<'a> { + /// The target that we're compiling for, used to query target-specific + /// information as necessary. + isa: &'a dyn TargetIsa, + + /// The object file that we're generating code into. obj: &'a mut Object, + + /// The WebAssembly module we're generating code for. module: &'a Module, - text_section: SectionId, - func_symbols: PrimaryMap, - jump_tables: PrimaryMap, + + /// Map of injected symbols for all possible libcalls, used whenever there's + /// a relocation against a libcall. libcalls: HashMap, - pending_relocations: Vec<(u64, &'a [Relocation])>, + + /// Packed form of windows unwind tables which, if present, will get emmited + /// to a windows-specific unwind info section. windows_unwind_info: Vec, + + /// Pending unwinding information for DWARF-based platforms. This is used to + /// build a `.eh_frame` lookalike at the very end of object building. systemv_unwind_info: Vec<(u64, &'a systemv::UnwindInfo)>, + + /// The corresponding symbol for each function, inserted as they're defined. + /// + /// If an index isn't here yet then it hasn't been defined yet. + func_symbols: PrimaryMap, + + /// `object`-crate identifier for the text section. + text_section: SectionId, + + /// The current offset within the `.text` section we're inserting at. + /// + /// Note that this does not match the text section offset in `obj`, but + /// rather the cumulative size of `text_contents` below. This kept separate + /// from `obj` because we edit the `text_contents` with relocations before + /// emitting them to the object. + current_text_off: u64, + + /// The segmented contents of the `.text` section, populated here as + /// functions are inserted. + /// + /// Note that the text section isn't actually concatenated until the very + /// end when after all relocations have been applied. Note that this is + /// separate from the section in `obj` because these contents will be edited + /// for relocations as necessary. + text_contents: Vec>, + + /// Map from text section offset, `u64`, to the index in `text_contents` + /// where those contents live. + text_locations: HashMap, + + /// A list of relocations that must be resolved before the + pending_relocs: Vec>, + + /// Offset, after which, some relocs in `pending_relocs` will no longer be + /// resolvable. This means that if code is added to the text section which + /// would go beyond this point then a stub must be inserted to resolve at + /// least one reloc. + reloc_deadline: u64, + + /// Relocations within veneers which exceed the native platform's relative + /// call instruction. These relocations will get filled in at the end + /// with relative offsets to their target. + relative_relocs: Vec, +} + +/// A pending relocation against a wasm-defined function that needs to be +/// resolved. These are collected as found in functions and then flushed via +/// `emit_jump_veneers` as necessary. +struct PendingReloc<'a> { + /// The maximum offset in the text section this relocation can jump to. + max_jump_distance: u64, + /// The offset in the text section for the function that contains this + /// relocation. + offset: u64, + /// The function that this relocation is against, or the target of the + /// original function call. + target: DefinedFuncIndex, + /// The relocation entry from the compiled function, describing the + /// relocation within the function at `offset`. + reloc: &'a Relocation, +} + +/// A relative relocation value within a veneer that will get resolved once all +/// functions have been emitted. One of these is inserted for each veneer +/// inserted and will get filled in in the `finish` method. +struct RelativeReloc { + /// The offset in the text section to the veneer that contains this + /// relocation. + veneer_offset: u64, + /// The offset, within the veneer, to where this relocation is located. + reloc_offset: usize, + /// The defined function this relocation is against. The relative distance + /// from the location of the relocation to this function's definition will + /// be written. + target: DefinedFuncIndex, + /// An optional addend for the relocation to add. + addend: i64, } // This is a mirror of `RUNTIME_FUNCTION` in the Windows API, but defined here @@ -116,7 +212,7 @@ struct RUNTIME_FUNCTION { } impl<'a> ObjectBuilder<'a> { - pub fn new(obj: &'a mut Object, module: &'a Module) -> Self { + pub fn new(obj: &'a mut Object, module: &'a Module, isa: &'a dyn TargetIsa) -> Self { // Entire code (functions and trampolines) will be placed // in the ".text" section. let text_section = obj.add_section( @@ -146,15 +242,20 @@ impl<'a> ObjectBuilder<'a> { let libcalls = write_libcall_symbols(obj); Self { + isa, obj, module, text_section, func_symbols, libcalls, - pending_relocations: Vec::new(), - jump_tables: PrimaryMap::with_capacity(module.functions.len()), + pending_relocs: Vec::new(), + reloc_deadline: u64::MAX, windows_unwind_info: Vec::new(), systemv_unwind_info: Vec::new(), + text_contents: Vec::new(), + current_text_off: 0, + text_locations: HashMap::new(), + relative_relocs: Vec::new(), } } @@ -162,14 +263,19 @@ impl<'a> ObjectBuilder<'a> { /// /// Returns the symbol associated with the function as well as the range /// that the function resides within the text section. - fn append_func(&mut self, name: Vec, func: &'a CompiledFunction) -> (SymbolId, Range) { - let off = self - .obj - .append_section_data(self.text_section, &func.body, 1); + fn append_func( + &mut self, + name: Vec, + func: &'a CompiledFunction, + body: Vec, + ) -> (SymbolId, Range) { + let body_len = body.len() as u64; + let off = self.push_code(body, true); + let symbol_id = self.obj.add_symbol(Symbol { name, value: off, - size: func.body.len() as u64, + size: body_len, kind: SymbolKind::Text, scope: SymbolScope::Compilation, weak: false, @@ -195,7 +301,7 @@ impl<'a> ObjectBuilder<'a> { .append_section_data(self.text_section, &unwind_info, 4); self.windows_unwind_info.push(RUNTIME_FUNCTION { begin: u32::try_from(off).unwrap(), - end: u32::try_from(off + func.body.len() as u64).unwrap(), + end: u32::try_from(off + body_len).unwrap(), unwind_address: u32::try_from(unwind_off).unwrap(), }); } @@ -209,30 +315,314 @@ impl<'a> ObjectBuilder<'a> { Some(_) => panic!("some unwind info isn't handled here"), None => {} } - if !func.relocations.is_empty() { - self.pending_relocations.push((off, &func.relocations)); + + for r in func.relocations.iter() { + let (symbol, symbol_offset) = match r.reloc_target { + // Relocations against user-defined functions means that this is + // a relocation against a module-local function. We want to + // resolve these relocations ourselves and not actually leave + // these to get resolve at load-time later. These relocations + // are all relative as well so there's no need to resolve them + // at load time when we can resolve them here at compile time. + RelocationTarget::UserFunc(index) => { + let mut r = PendingReloc { + target: self.module.defined_func_index(index).unwrap(), + offset: off, + reloc: r, + max_jump_distance: 0, + }; + + // Determine this `reloc`'s deadline. This is the maximal + // forward distance for the relocation itself added to the + // position of the relocation, adjusted to assume that + // every pending relocation pessimistically needsd a + // veneer. + let offset_to_edit = r.offset + u64::from(r.reloc.offset); + r.max_jump_distance = offset_to_edit + r.limits().1; + + self.enqueue_reloc(r); + continue; + } + + // These relocations, unlike against user funcs above, typically + // involve absolute addresses and need to get resolved at load + // time. These are persisted immediately into the object file. + // + // FIXME: these, like user-defined-functions, should probably + // use relative jumps and avoid absolute relocations. They don't + // seem too common though so aren't necessarily that important + // to optimize. + RelocationTarget::LibCall(call) => (self.libcalls[&call], 0), + RelocationTarget::JumpTable(jt) => (symbol_id, func.jt_offsets[jt]), + }; + let (kind, encoding, size) = match r.reloc { + Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), + Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), + other => unimplemented!("Unimplemented relocation {:?}", other), + }; + self.obj + .add_relocation( + self.text_section, + ObjectRelocation { + offset: off + r.offset as u64, + size, + kind, + encoding, + symbol, + addend: r.addend.wrapping_add(symbol_offset as i64), + }, + ) + .unwrap(); + } + (symbol_id, off..off + body_len) + } + + /// Inserts a chunk of code into the text section. + /// + /// This method is required to update various internal data structures + /// about the structure of the text section. The most important part handled + /// by this function is insertion of "jump veneers". If a previous + /// function's relative call may reach further than the function being + /// inserted, then a "veneer" is inserted to help it jump further, but the + /// veneer needs to be inserted before `body` is inserted. + fn push_code(&mut self, body: Vec, allow_veneers: bool) -> u64 { + let body_len = body.len() as u64; + assert!(body_len > 0); + + // If this function would exceed the `reloc_deadline`, then all + // relocations are processed to force some veneers to get generated. + while self.current_text_off + body_len >= self.reloc_deadline { + assert!(allow_veneers); + self.emit_jump_veneers(self.current_text_off + body_len); + } + + // Once we can safely append the body we do so, updating various state + // about how to get back to the body. + let ret = self.current_text_off; + self.text_locations.insert(ret, self.text_contents.len()); + self.current_text_off += body_len; + self.text_contents.push(body); + return ret; + } + + /// Enqueues a `reloc` to get processed later. + /// + /// This function is used to push a `PendingReloc` record onto the list of + /// relocs that need to get processed at some point in the future. This will + /// update internal state about when to look at the relocs list and possibly + /// emit jump veneers based on the relocation and the maximum distance that + /// it can travel. + fn enqueue_reloc(&mut self, reloc: PendingReloc<'a>) { + // Decrease the deadline to emit veneers by pessimistically assuming + // that this relocation will need a veneer. + self.reloc_deadline -= max_jump_veneer_size(self.isa) as u64; + + // Insert the relocation into our pending list... + let max = reloc.max_jump_distance; + self.pending_relocs.push(reloc); + + // ... and finally possibly recalculate the relocation deadline based + // on whether this relocation is shorter than some previous relocation. + // Like above we pessimistically assume that all relocations need + // veneers when calculating the deadline. + let reloc_deadline = + max - (self.pending_relocs.len() * max_jump_veneer_size(self.isa)) as u64; + self.reloc_deadline = self.reloc_deadline.min(reloc_deadline); + } + + /// Generates "jump veneers" as necessary, or otherwise handles and patches + /// relative relocations. + /// + /// This function is the main workhorse of processing relative relocations + /// desired within functions. These relative relocations are typically + /// calls between wasm functions, but the distance on these calls is bounded + /// based on the architecture in question. On the smaller end, for example, + /// AArch64 calls can go at most 64MB either forwards or backwards. This + /// function handles the case where two functions are more than 64MB apart, + /// for example. + /// + /// Internally this function will process all entries in `pending_relocs` + /// and handle them as necessary. For each pending relocation it can fall + /// in a number of categories: + /// + /// * First the relocation could be against a known symbol, and the distance + /// from the relocation to the symbol is in-bounds. This means we can + /// simply patch the code directly and then we're good to go. + /// + /// * Second the relocation could be against a known symbol, but it could be + /// too far away to fit within the relocation's limits. This means that a + /// veneer is generated to jump to the destination. + /// + /// * Third a relocation could be against an unknown symbol, meaning it's a + /// function that hasn't been defined yet. If we're still within the jump + /// range, though, no action needs to be taken, and the relocation is + /// enqueued for processing later. + /// + /// * Finally a relocation against an unknown symbol may be so far away + /// that if the next symbol is inserted it couldn't reach its + /// destination. In this situation a veneer is generated. + fn emit_jump_veneers(&mut self, forced_threshold: u64) { + // Reset the relocation deadline since we're handling all relocations. + // This will get updated in recursive calls to `enqueue_reloc` if + // necessary. + self.reloc_deadline = u64::MAX; + + let before = self.pending_relocs.len(); + for r in mem::take(&mut self.pending_relocs) { + let target = self.module.func_index(r.target); + match self.func_symbols.get(target) { + Some(sym) => { + let sym_off = self.obj.symbol(*sym).value; + let distance = r.relative_distance_to(sym_off); + let (min_neg, max_pos) = r.limits(); + + // This is the second case described above. A relocation was + // added for a symbol but the symbol was defined very long + // ago. The only way to reach the symbol at this point + // is via a veneer. + if distance < min_neg { + self.emit_jump_veneer(r); + + // This case, if hit, represents a programming error. If + // the forward distance to the symbol is too large that + // means that this function wasn't called soon enough to + // insert a veneer, or something about the calculations of + // `forced_threshold` is wrong. + } else if distance > (max_pos as i64) { + panic!("should have emitted island earlier"); + + // This is the first case describe above. The distance to + // the symbol naturally fits within the limits of the + // relocation, so we can simply patch the distance in and + // the relocation is resolved. + } else { + self.patch_reloc(&r, distance); + } + } + + // Function not defined, meaning it will come later. If the + // reloc can't jump over the `forced_threshold` then we must + // insert a veneer. Otherwise we can wait for the next batch + // of veneers and continue to the next reloc. + None => { + if forced_threshold < r.max_jump_distance { + self.enqueue_reloc(r); + } else { + self.emit_jump_veneer(r); + } + } + } + } + + // At least one pending relocation should have been processed. + assert!( + self.pending_relocs.len() < before, + "no relocations processed" + ); + } + + /// Returns the `object`-crate's idea of endianness for the configured + /// target. + fn endian(&self) -> object::Endianness { + match self.isa.triple().endianness().unwrap() { + target_lexicon::Endianness::Little => object::Endianness::Little, + target_lexicon::Endianness::Big => object::Endianness::Big, + } + } + + /// Patches a relocation with the `value` specified. + /// + /// This method is the implementation detail of actually modifying code + /// emitted by Cranelift by patching in values to relocations. By doing + /// this at object-assembly time here we can avoid doing this at load-time + /// later, frontloading as much work as possible to make cache loads more + /// efficient. + fn patch_reloc(&mut self, r: &PendingReloc<'a>, value: i64) { + type U32 = object::U32Bytes; + type I32 = object::I32Bytes; + + // sanity-check + let (min_neg, max_pos) = r.limits(); + assert!(min_neg <= value && value <= (max_pos as i64)); + + let endian = self.endian(); + let code = self.text_locations[&r.offset]; + let code = &mut self.text_contents[code]; + + match r.reloc.reloc { + // This corresponds to the `R_AARCH64_CALL26` ELF relocation. + Reloc::Arm64Call => { + let reloc_address = reloc_address::(code, r.reloc.offset); + let bits = (value as u32) >> 2; + let insn = reloc_address.get(endian); + let new_insn = (insn & 0xfc00_0000) | (bits & 0x03ff_ffff); + reloc_address.set(endian, new_insn); + } + + // This corresponds to the `R_386_PC32` ELF relocation. + Reloc::X86CallPCRel4 => { + reloc_address::(code, r.reloc.offset).set(endian, value as i32); + } + + // This corresponds to the `R_390_PC32DBL` ELF relocation. + Reloc::S390xPCRel32Dbl => { + reloc_address::(code, r.reloc.offset).set(endian, (value as i32) >> 1); + } + + other => panic!("unsupported function reloc {:?}", other), } - (symbol_id, off..off + func.body.len() as u64) } - /// Pushes a new defined function from the a wasm module into this object, - /// returning the range that the compiled code will live at relative in the - /// text section of the final executable. + /// Emits a "jump veneer" at the current position in the code to resolve + /// the relocation `r`. /// - /// Note that functions must be pushed in the order of their - /// `DefinedFuncIndex`. - pub fn func(&mut self, index: DefinedFuncIndex, func: &'a CompiledFunction) -> Range { - assert_eq!(self.jump_tables.push(&func.jt_offsets), index); + /// This function will ask the configured `TargetIsa` to generate a veneer + /// for us and then that veneer will be inserted into the text section. + /// Afterwards we update internal metadata to record that there's a relocation + /// we need to update when all symbols are defined (since all veneers + /// contain a 64-bit relative offset) and the original relocation needs to be + /// patched to jump to the veneer we're synthesizing. + fn emit_jump_veneer(&mut self, r: PendingReloc<'a>) { + let (veneer, reloc_offset) = generate_jump_veneer(self.isa); + let veneer_offset = self.push_code(veneer, false); + self.relative_relocs.push(RelativeReloc { + veneer_offset, + reloc_offset, + target: r.target, + addend: i64::from(r.reloc.addend), + }); + self.patch_reloc(&r, r.relative_distance_to(veneer_offset)); + } + + /// Appends a function to this object file. + /// + /// This is expected to be called in-order for ascending `index` values. + pub fn func( + &mut self, + index: DefinedFuncIndex, + func: &'a CompiledFunction, + code: Vec, + ) -> Range { let index = self.module.func_index(index); let name = obj::func_symbol_name(index); - let (symbol_id, range) = self.append_func(name.into_bytes(), func); + let (symbol_id, range) = self.append_func(name.into_bytes(), func, code); assert_eq!(self.func_symbols.push(symbol_id), index); range } - pub fn trampoline(&mut self, sig: SignatureIndex, func: &'a CompiledFunction) { + /// Helper function exclusively for tests to increase padding between + /// functions to test the veneer insertion logic in this file. + pub fn append_synthetic_padding(&mut self, amt: usize) { + self.push_code(vec![0; amt], true); + } + + /// Inserts a compiled trampoline into this object. + /// + /// This is expected to be called after all the defined functions of a wasm + /// file have been inserted. + pub fn trampoline(&mut self, sig: SignatureIndex, func: &'a CompiledFunction, code: Vec) { let name = obj::trampoline_symbol_name(sig); - self.append_func(name.into_bytes(), func); + self.append_func(name.into_bytes(), func, code); } pub fn align_text_to(&mut self, align: u64) { @@ -284,80 +674,54 @@ impl<'a> ObjectBuilder<'a> { Ok(()) } - pub fn finish(&mut self, isa: &dyn TargetIsa) -> Result<()> { - self.append_relocations()?; + pub fn finish(&mut self) -> Result<()> { + // If there are any more pending relocations we have yet to resolve, + // then we force them all to be resolved by setting the deadline for + // emission at 0. No more functions are coming so now's the time to + // flush all of them, if any. + if !self.pending_relocs.is_empty() { + self.emit_jump_veneers(0); + assert!(self.pending_relocs.is_empty()); + } + + // Once we've handled all relocations between functions we can now + // process all of the relocations which are required inside the veneers + // themselves (if any). This is is only possible once the address of + // all symbols are know, and here we're patching in the relative distance + // between the immediate in the veneer and the destination it's + // supposed to go to. This technique allows us to not actually generate + // any relocations in the object file itself, since everything is + // relative. + let endian = self.endian(); + for reloc in mem::take(&mut self.relative_relocs) { + // Calculate the actual value that will be stored in the relocation + // location. + let target = self.module.func_index(reloc.target); + let symbol = self.func_symbols[target]; + let target_offset = self.obj.symbol(symbol).value; + let reloc_offset = reloc.veneer_offset + (reloc.reloc_offset as u64); + let value = target_offset + .wrapping_add(reloc.addend as u64) + .wrapping_sub(reloc_offset); + + // Store the `value` into the location specified in the relocation. + let code = &mut self.text_contents[self.text_locations[&reloc.veneer_offset]]; + assert_eq!(self.isa.pointer_type().bits(), 64); + reloc_address::>(code, reloc.reloc_offset as u32) + .set(endian, value); + } + + // With relocations all handled we can shove everything into the final + // text section now. + for contents in self.text_contents.iter() { + self.obj.append_section_data(self.text_section, contents, 1); + } + if self.windows_unwind_info.len() > 0 { self.append_windows_unwind_info(); } if self.systemv_unwind_info.len() > 0 { - self.append_systemv_unwind_info(isa); - } - Ok(()) - } - - fn append_relocations(&mut self) -> Result<()> { - for (off, relocations) in self.pending_relocations.iter() { - for r in relocations.iter() { - let (symbol, symbol_offset) = match r.reloc_target { - RelocationTarget::UserFunc(index) => (self.func_symbols[index], 0), - RelocationTarget::LibCall(call) => (self.libcalls[&call], 0), - RelocationTarget::JumpTable(f, jt) => { - let df = self.module.defined_func_index(f).unwrap(); - let offset = *self - .jump_tables - .get(df) - .and_then(|t| t.get(jt)) - .expect("func jump table"); - (self.func_symbols[f], offset) - } - }; - let (kind, encoding, size) = match r.reloc { - Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), - Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), - Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32), - Reloc::X86CallPCRel4 => { - (RelocationKind::Relative, RelocationEncoding::X86Branch, 32) - } - // TODO: Get Cranelift to tell us when we can use - // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX. - Reloc::X86CallPLTRel4 => ( - RelocationKind::PltRelative, - RelocationEncoding::X86Branch, - 32, - ), - Reloc::X86GOTPCRel4 => { - (RelocationKind::GotRelative, RelocationEncoding::Generic, 32) - } - Reloc::ElfX86_64TlsGd => ( - RelocationKind::Elf(elf::R_X86_64_TLSGD), - RelocationEncoding::Generic, - 32, - ), - Reloc::X86PCRelRodata4 => { - continue; - } - Reloc::Arm64Call => ( - RelocationKind::Elf(elf::R_AARCH64_CALL26), - RelocationEncoding::Generic, - 32, - ), - Reloc::S390xPCRel32Dbl => { - (RelocationKind::Relative, RelocationEncoding::S390xDbl, 32) - } - other => unimplemented!("Unimplemented relocation {:?}", other), - }; - self.obj.add_relocation( - self.text_section, - ObjectRelocation { - offset: off + r.offset as u64, - size, - kind, - encoding, - symbol, - addend: r.addend.wrapping_add(symbol_offset as i64), - }, - )?; - } + self.append_systemv_unwind_info(); } Ok(()) } @@ -432,14 +796,15 @@ impl<'a> ObjectBuilder<'a> { /// This allows `.eh_frame` to have different virtual memory permissions, /// such as being purely read-only instead of read/execute like the code /// bits. - fn append_systemv_unwind_info(&mut self, isa: &dyn TargetIsa) { + fn append_systemv_unwind_info(&mut self) { let segment = self.obj.segment_name(StandardSegment::Data).to_vec(); let section_id = self.obj.add_section( segment, b"_wasmtime_eh_frame".to_vec(), SectionKind::ReadOnlyData, ); - let mut cie = isa + let mut cie = self + .isa .create_systemv_cie() .expect("must be able to create a CIE for system-v unwind info"); let mut table = FrameTable::default(); @@ -460,7 +825,7 @@ impl<'a> ObjectBuilder<'a> { let fde = unwind_info.to_fde(Address::Constant(actual_offset as u64)); table.add_fde(cie_id, fde); } - let endian = match isa.triple().endianness().unwrap() { + let endian = match self.isa.triple().endianness().unwrap() { target_lexicon::Endianness::Little => RunTimeEndian::Little, target_lexicon::Endianness::Big => RunTimeEndian::Big, }; @@ -521,3 +886,54 @@ impl<'a> ObjectBuilder<'a> { } } } + +impl PendingReloc<'_> { + /// Returns the maximum negative offset and maximum positive offset that + /// this relocation can reach. + fn limits(&self) -> (i64, u64) { + match self.reloc.reloc { + Reloc::Arm64Call => (-(1 << 27), 1 << 27), + Reloc::X86CallPCRel4 => (i32::MIN.into(), i32::MAX as u64), + Reloc::S390xPCRel32Dbl => (i32::MIN.into(), i32::MAX as u64), + + other => panic!("unsupported function reloc {:?}", other), + } + } + + /// Returns the relative distance from this relocation to the `offset` + /// specified in the text section. + fn relative_distance_to(&self, offset: u64) -> i64 { + (offset as i64) + .wrapping_add(self.reloc.addend) + .wrapping_sub((self.offset + u64::from(self.reloc.offset)) as i64) + } +} + +fn max_jump_veneer_size(isa: &dyn TargetIsa) -> usize { + match isa.get_mach_backend() { + Some(backend) => backend.max_jump_veneer_size(), + // Old-style backends don't have veneers, and we'll panic if we need + // them, so the size is zero. + None => 0, + } +} + +fn generate_jump_veneer(isa: &dyn TargetIsa) -> (Vec, usize) { + match isa.get_mach_backend() { + Some(backend) => backend.generate_jump_veneer(), + // This isn't implemented for the old backends, so we just don't + // support objects of this size. + None => panic!("jump veneers only supported on new backend"), + } +} + +/// Returns the address of `T` within `code` at `offset`, used to get the +/// address of where a relocation needs to be written. +fn reloc_address(code: &mut [u8], offset: u32) -> &mut T { + let (reloc, _rest) = usize::try_from(offset) + .ok() + .and_then(move |offset| code.get_mut(offset..)) + .and_then(|range| object::from_bytes_mut(range).ok()) + .expect("invalid reloc offset"); + reloc +} diff --git a/crates/fuzzing/foo.o b/crates/fuzzing/foo.o new file mode 100644 index 0000000000000000000000000000000000000000..be5a8749e0a19196bf8d9340d1f587c0114a62d9 GIT binary patch literal 10000 zcmeI2J%|%Q6vyB0o)-&G1InQiz4+D0apXP_vCsrm5Cp|WyLEHfkcE86?&geMVY+J- zwGh<$JX=Ao5EU%!1Pf~gOTi+|T@m!n?hLy#*?|-mC%l2p&ilRpdo%lAk~H(+($&kR zVSpG0yoDqZ6yW-)WL}P@Wyrx~0%=ALkOSlZIY17O1LOcXKn{=tvybqv@vpk93ThC0djyGAP2|+a)2Bl2gm_(;6HKT0N-iXcf1&Q-n0@2!`p>g?NWx5=lo0+L;#|bSk2TK4^N8nfa9-w1 ziToALYn%)HY0mqKeg%2nEaK8X*O z^}Oda`gaqmBBf$vi`fe*}6lo;F_OkG$HMX9M;&wjtvS|%^@`dd7?3mMz%ZkEJxFuq0qCl;TlZ@m+7^i)|;3E?oxvg)V! N`L*-urZ`vY{{nlHSfc;{ literal 0 HcmV?d00001 diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index 1757935179c6..a2a7a165c929 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -182,6 +182,7 @@ impl CompilationArtifacts { push_debug(&mut obj, &debuginfo.debug_ranges); push_debug(&mut obj, &debuginfo.debug_rnglists); } + std::fs::write("foo.o", &obj.write()?).unwrap(); // Encode a `CompiledModuleInfo` structure into the `ELF_WASMTIME_INFO` // section of this image. This is not necessary when the returned module diff --git a/crates/jit/src/link.rs b/crates/jit/src/link.rs index 580373c1bd2d..c7a5dce4f5c1 100644 --- a/crates/jit/src/link.rs +++ b/crates/jit/src/link.rs @@ -1,12 +1,10 @@ //! Linking for JIT-compiled code. use object::read::{Object, ObjectSection, Relocation, RelocationTarget}; -use object::{elf, File, NativeEndian as NE, ObjectSymbol, RelocationEncoding, RelocationKind}; +use object::{File, NativeEndian as NE, ObjectSymbol, RelocationEncoding, RelocationKind}; use std::convert::TryFrom; use wasmtime_runtime::libcalls; -type U32 = object::U32Bytes; -type I32 = object::I32Bytes; type U64 = object::U64Bytes; /// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`. @@ -27,31 +25,26 @@ pub fn link_module(obj: &File, code_range: &mut [u8]) { } fn apply_reloc(obj: &File, code: &mut [u8], offset: u64, r: Relocation) { - let target_func_address: usize = match r.target() { - RelocationTarget::Symbol(i) => { - // Processing relocation target is a named symbols that is compiled - // wasm function or runtime libcall. - let sym = obj.symbol_by_index(i).unwrap(); - if sym.is_local() { - &code[sym.address() as usize] as *const u8 as usize - } else { - match sym.name() { - Ok(name) => { - if let Some(addr) = to_libcall_address(name) { - addr - } else { - panic!("unknown function to link: {}", name); - } - } - Err(_) => panic!("unexpected relocation target: not a symbol"), + let sym = match r.target() { + RelocationTarget::Symbol(i) => obj.symbol_by_index(i).unwrap(), + _ => panic!("unexpected relocation target"), + }; + let target_func_address: usize = if sym.is_local() { + &code[sym.address() as usize] as *const u8 as usize + } else { + match sym.name() { + Ok(name) => { + if let Some(addr) = to_libcall_address(name) { + addr + } else { + panic!("unknown function to link: {}", name); } } + Err(_) => panic!("unexpected relocation target: not a symbol"), } - _ => panic!("unexpected relocation target"), }; match (r.kind(), r.encoding(), r.size()) { - #[cfg(target_pointer_width = "64")] (RelocationKind::Absolute, RelocationEncoding::Generic, 64) => { let reloc_address = reloc_address::(code, offset); let reloc_abs = (target_func_address as u64) @@ -59,63 +52,6 @@ fn apply_reloc(obj: &File, code: &mut [u8], offset: u64, r: Relocation) { .unwrap(); reloc_address.set(NE, reloc_abs); } - #[cfg(target_pointer_width = "32")] - (RelocationKind::Relative, RelocationEncoding::Generic, 32) => { - let reloc_address = reloc_address::(code, offset); - let reloc_delta_u32 = (target_func_address as u32) - .wrapping_sub(reloc_address as *const _ as u32) - .checked_add(r.addend() as u32) - .unwrap(); - reloc_address.set(NE, reloc_delta_u32); - } - #[cfg(target_pointer_width = "32")] - (RelocationKind::Relative, RelocationEncoding::X86Branch, 32) => { - let reloc_address = reloc_address::(code, offset); - let reloc_delta_u32 = (target_func_address as u32) - .wrapping_sub(reloc_address as *const _ as u32) - .wrapping_add(r.addend() as u32); - reloc_address.set(NE, reloc_delta_u32); - } - #[cfg(target_pointer_width = "64")] - (RelocationKind::Relative, RelocationEncoding::Generic, 32) => { - let reloc_address = reloc_address::(code, offset); - let reloc_delta_i64 = (target_func_address as i64) - .wrapping_sub(reloc_address as *const _ as i64) - .wrapping_add(r.addend()); - // TODO implement far calls mode in x64 new backend. - reloc_address.set( - NE, - i32::try_from(reloc_delta_i64).expect("relocation too large to fit in i32"), - ); - } - #[cfg(target_pointer_width = "64")] - (RelocationKind::Relative, RelocationEncoding::S390xDbl, 32) => { - let reloc_address = reloc_address::(code, offset); - let reloc_delta_i64 = (target_func_address as i64) - .wrapping_sub(reloc_address as *const _ as i64) - .wrapping_add(r.addend()) - >> 1; - reloc_address.set( - NE, - i32::try_from(reloc_delta_i64).expect("relocation too large to fit in i32"), - ); - } - (RelocationKind::Elf(elf::R_AARCH64_CALL26), RelocationEncoding::Generic, 32) => { - let reloc_address = reloc_address::(code, offset); - let reloc_delta = (target_func_address as u64).wrapping_sub(r.addend() as u64); - // TODO: come up with a PLT-like solution for longer calls. We can't extend the - // code segment at this point, but we could conservatively allocate space at the - // end of the function during codegen, a fixed amount per call, to allow for - // potential branch islands. - assert!((reloc_delta as i64) < (1 << 27)); - assert!((reloc_delta as i64) >= -(1 << 27)); - let reloc_delta = reloc_delta as u32; - let reloc_delta = reloc_delta.wrapping_add(r.addend() as u32); - let delta_bits = reloc_delta >> 2; - let insn = reloc_address.get(NE); - let new_insn = (insn & 0xfc00_0000) | (delta_bits & 0x03ff_ffff); - reloc_address.set(NE, new_insn); - } other => panic!("unsupported reloc kind: {:?}", other), } } diff --git a/crates/test-programs/foo.o b/crates/test-programs/foo.o new file mode 100644 index 0000000000000000000000000000000000000000..ba86c05b560f2a12c872e069095f37c98400f949 GIT binary patch literal 169344 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%0A*kM|Gk7b?r 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 Imh<`lH%BW|H~;_u literal 0 HcmV?d00001 diff --git a/crates/wasmtime/foo.o b/crates/wasmtime/foo.o new file mode 100644 index 0000000000000000000000000000000000000000..5aa5f90d777545f33ac8c0b7fbeec3ef29514122 GIT binary patch literal 9760 zcmeI2ziSjh6vyB0o;iy|7a~E$m?dZ<63!Sb6!b39fDjPG%4Toxvdb>qk8ryeqbURt zti{Sgu}Bf)AD|YZ{slIY+SC>b*KBjdH@h>pKkgJ<~*X=KwxhwjyI(ZiN)1n?#6+Wi(4y}Q7Fmiw#AP2|+a)2Bl2gm_(fE*wP$N_TT zKXBj#Kcn*KE)+J-D*E_LL@wgz8Js_U?MGvc>tJeDDOHZ?uhiGd^2BuIMInB`t_Q5( zG=9f=Q8%rxOiTl-8{i!0y1})AR9(~!>Z0_(aSAUhsHqzZ;+*$67qK|cHO`;%s-pjG zVkwL24jR?#wR8EmlFO?!uKS+E8)*1{F{W=Bzz3N*r@M&RighS;ddmwC*kKM z93EJHGw_0MbcyQ|)MmiQUspr&P89or8QnR9u51Vp9NXtz6AT@FP(eK#dy#2+Hv zj@?WOMVbs@=c?OYBhj!!WeLmjd^fE@5_Gz)dNwUaCNsu#@M+++Tjo6Ez$L(3tGU*0 zcrDw+D621J9+v>-0(&EfRgvAcYdV;3!<_H>K_{?kxD)HcW)!oMt5#s)Z}+c*{h!zz zx@n~I%3>9ba&eL85B*h*IAKfV@s;&Tj|B`E%ja(yX;re%=d<@+K9{Mt4fdJ5bB^G? ofb%GWI8Me3u=kV3_vz)sY#h0{hPe<W7yD1s-J}?_}h>vt7#loCUZ6(Ea*DfnK>Y>l9$l*-(^T2d;b@tbTg&QPD7p>o&3O zL1)hCz&zHadfkM&{(*64Q;~ap>|2!cYXw<-$NCp!SnH=%hu??Smt^&Al7813O&3NE zkOSlZIY17O1LOcXKn{=t@-mt#2_k$&pvnEE*7VWxk|(!bPuRpR3r ze5pZ>yx9=k&rzQT>mc6p`I6r Result> { + let mut config = Config::new(); + // This is an internal debug-only setting specifically recognized for + // basically just this set of tests. + unsafe { + config.cranelift_flag_set("padding_between_functions", &padding.to_string())?; + } + let engine = Engine::new(&config)?; + Ok(Store::new(&engine, ())) +} + +#[test] +fn forward_call_works() -> Result<()> { + let mut store = store_with_padding(128 * MB)?; + let module = Module::new( + store.engine(), + r#" + (module + (func (export "foo") (result i32) + call 1) + (func (result i32) + i32.const 4) + ) + "#, + )?; + + let i = Instance::new(&mut store, &module, &[])?; + let foo = i.get_typed_func::<(), i32, _>(&mut store, "foo")?; + assert_eq!(foo.call(&mut store, ())?, 4); + Ok(()) +} + +#[test] +fn backwards_call_works() -> Result<()> { + let mut store = store_with_padding(128 * MB)?; + let module = Module::new( + store.engine(), + r#" + (module + (func (result i32) + i32.const 4) + (func (export "foo") (result i32) + call 0) + ) + "#, + )?; + + let i = Instance::new(&mut store, &module, &[])?; + let foo = i.get_typed_func::<(), i32, _>(&mut store, "foo")?; + assert_eq!(foo.call(&mut store, ())?, 4); + Ok(()) +} + +#[test] +fn mixed() -> Result<()> { + let mut store = store_with_padding(MB)?; + + const N: i32 = 200; + + let mut wat = String::new(); + wat.push_str("(module\n"); + wat.push_str("(func $first (result i32) (i32.const 1))\n"); + for i in 0..N { + wat.push_str(&format!("(func (export \"{}\") (result i32 i32)\n", i)); + wat.push_str("call $first\n"); + wat.push_str(&format!("i32.const {}\n", i)); + wat.push_str("i32.add\n"); + wat.push_str("call $last\n"); + wat.push_str(&format!("i32.const {}\n", i)); + wat.push_str("i32.add)\n"); + } + wat.push_str("(func $last (result i32) (i32.const 2))\n"); + wat.push_str(")\n"); + + let module = Module::new(store.engine(), &wat)?; + + let instance = Instance::new(&mut store, &module, &[])?; + + for i in 0..N { + let name = i.to_string(); + let func = instance.get_typed_func::<(), (i32, i32), _>(&mut store, &name)?; + let (a, b) = func.call(&mut store, ())?; + assert_eq!(a, i + 1); + assert_eq!(b, i + 2); + } + Ok(()) +} + +#[test] +fn forward_huge() -> Result<()> { + let mut store = store_with_padding(2 * GB)?; + let module = Module::new( + store.engine(), + r#" + (module + (func (export "foo") (result i32) + call 1) + (func (result i32) + i32.const 4) + ) + "#, + )?; + + let i = Instance::new(&mut store, &module, &[])?; + let foo = i.get_typed_func::<(), i32, _>(&mut store, "foo")?; + assert_eq!(foo.call(&mut store, ())?, 4); + Ok(()) +} From 9588abad082e3c5bc03002d9d7a012c0d3edc040 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 26 Aug 2021 16:40:08 -0700 Subject: [PATCH 02/17] Remove stray changes --- crates/bench-api/foo.o | Bin 9672 -> 0 bytes crates/fuzzing/foo.o | Bin 10000 -> 0 bytes crates/jit/src/instantiate.rs | 1 - crates/test-programs/foo.o | Bin 169344 -> 0 bytes crates/wasmtime/foo.o | Bin 9760 -> 0 bytes crates/wiggle/foo.o | Bin 9840 -> 0 bytes 6 files changed, 1 deletion(-) delete mode 100644 crates/bench-api/foo.o delete mode 100644 crates/fuzzing/foo.o delete mode 100644 crates/test-programs/foo.o delete mode 100644 crates/wasmtime/foo.o delete mode 100644 crates/wiggle/foo.o diff --git a/crates/bench-api/foo.o b/crates/bench-api/foo.o deleted file mode 100644 index 230f693dca444a20edc0714bd2b10edaee04c68f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9672 zcmeI2zi-n(6vv!Iz<8@CKltQwqhwxs`%0dIxsRL zCI%2Als|w8@nd9RL7nN$08$n&QF&+kuzks~uvF?h$-ehK{=RpAHW(f8 z(tfWi^dA)bj)D&r{Gx)xmJ7wqp0!{mmi?3&cZB$8~yc$TFFCb$J3rrPuCu zdqtgu;x)roPQ*z#M*2NB$Qs3(0^#q~bp3Xw;V)Gryvh$-c?}ZQ>vx*PvIN=O7~8|A z%`ZfSgz;)cXBOP y4=Vf)5Tyl2FxK&!;Sk5EWCH&G$GJQ<78CM7T2M;i49X_ee?6bn-;^55^?v~F*zj2Z diff --git a/crates/fuzzing/foo.o b/crates/fuzzing/foo.o deleted file mode 100644 index be5a8749e0a19196bf8d9340d1f587c0114a62d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10000 zcmeI2J%|%Q6vyB0o)-&G1InQiz4+D0apXP_vCsrm5Cp|WyLEHfkcE86?&geMVY+J- zwGh<$JX=Ao5EU%!1Pf~gOTi+|T@m!n?hLy#*?|-mC%l2p&ilRpdo%lAk~H(+($&kR zVSpG0yoDqZ6yW-)WL}P@Wyrx~0%=ALkOSlZIY17O1LOcXKn{=tvybqv@vpk93ThC0djyGAP2|+a)2Bl2gm_(;6HKT0N-iXcf1&Q-n0@2!`p>g?NWx5=lo0+L;#|bSk2TK4^N8nfa9-w1 ziToALYn%)HY0mqKeg%2nEaK8X*O z^}Oda`gaqmBBf$vi`fe*}6lo;F_OkG$HMX9M;&wjtvS|%^@`dd7?3mMz%ZkEJxFuq0qCl;TlZ@m+7^i)|;3E?oxvg)V! N`L*-urZ`vY{{nlHSfc;{ diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index a2a7a165c929..1757935179c6 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -182,7 +182,6 @@ impl CompilationArtifacts { push_debug(&mut obj, &debuginfo.debug_ranges); push_debug(&mut obj, &debuginfo.debug_rnglists); } - std::fs::write("foo.o", &obj.write()?).unwrap(); // Encode a `CompiledModuleInfo` structure into the `ELF_WASMTIME_INFO` // section of this image. This is not necessary when the returned module diff --git a/crates/test-programs/foo.o b/crates/test-programs/foo.o deleted file mode 100644 index ba86c05b560f2a12c872e069095f37c98400f949..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169344 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%0A*kM|Gk7b?r zzluLXYclzWPBLHoD8nD|q9cEVoiF5%5dY*Ok>5r>6U`TywsWR0Bt4&4e}rsRb&52O3Y#^!q{#)irCJ;;W+Sl@$em>6=UwX5VzzK1Sk^8NtdgZh2| z8|Fp)3|eb-Ad6ai$?qZFX8IH)ljy^`BMVMkmQNvpUX)K^Ja)tXUwjH@$%p!N`xG4d zM(ffY^v&h?6dd|G3?6SiIaa@d>i3uX)CZvRE&gTu0Lt+BU@uM3zF@}s(W&G>_1!zW z{!}>JwuiB=_4PS?PdZ;z>uTN^UN_r`v?*6HvE1&b*}cH-r&+{)nqG~Iv*r9!sWDD!vn9h`Lljk`5rW4#ta=t@fn0#`6 zcacZYg{}NDzsV=+?W117CtP?F_f6xush{8e^C9m2<@v{Q8DFYPXF2vUr$b|2AHT!g zH|Ma*9_4u*skT|3u*$#@o;`#k&|E7f{h>J z9)8T;{%|NS%5Tl*qTS(l6dmmG_%>+jWB+~Tp7WIb>s!+#MzfWDu$~PZx~z1AE_bZOaQ>sLzR|B!ELz2me! zXK1l<_cpYsjxv1Ap$T31Zm%J(r~UrOIkKrd7M+vg?K6q97IGF(s=r_#zmFf;c&GYD zEO(OgKRB0SH2fZKg81RCw|ewWkhU?N5#aZeX{{yMl%MQ7$Uri64)itqreU|SCMt<* z?_j79ceOn1-i_nu89KYbdwoc#>g{A8{_x9ggHan`;m+0&UQJ|6n< zU#)YQ=#PH%PS6@`cwW}Xct_%0=6Mi`Q@3<;{=*T;L&Th8F`7Jz|7FChL(f}ZFKyiC zz&B5RI*jbucc?`!p?@-I^v zFw=V8^1&Pj<^dNb>DJ}-h0d{;%#C<1k52|YS;^E%LE}z($ok4HfBp#?JQ~@*{@70g z@CM?kfAI&xQazEXt_6$hLRipZv=QDWz^IG0n#Ji!rd)IPr`lR$(|7wT! zNuI^;GmUrn2>$@(8yw^`$Kc!9JDu|s&HJW*Y2SyQ(hI(GrM&(N z;E}+df4y5Dqp?#XpZh-a+-^=0A!M-STW~>Aq za1>vxneX|{o_y(pNRMLTIp_}Wnllx8UaoE|WsP6jimihmX>tJ{#UA?LM-e!5)=V?_ z1p7a*zdE_6^C|A1;~af4>1=BPPS5@b+|A(FO-@T8bC}Mq=;YlGx37JgeGxhP61@8% z-*KF?e2#=1M`a$NOayvvkIGNeSb7KFZ$ryQ?2C|MaKTAqC?G@<0{)i?CM zPuy_A7i_uZZn=j{xf^=lSx8Pyxyo(mo#&RzrQ8N^K1F=vWSO)26!u0g?;|JwBCkLn zXwp8cb#8l7^k=l|>0Ic~%|icICPDwCVETDh%dj=*ku8h!o-a*gYj(_uh>bjoK9pqEoL453f z+HyUg)b&EH`FPhJ|Ir7ll|%f>FV7nt==BFNId)~`)vp=Sb`Y*M; z=vnsphkHCb!rylPeQ%x!ny+MEXf34}b|ddUP`)#35%AhGQWY0LZ|_fpojkv-k;t*@ zp9C}rW+J>z!uxRbj#ADUDtQb0Z$17y&hBp5k;A*@fmhB(n4As43pUEy^)1#e?1id* z`_zFoCr`a-@_7~ee5x;GWs38@=ERe0=EU)3?266oazeG4Wwd{bGnXcGQAY6)=zZR< zSDSh(;BO|pa)$kNwEpt^(F$~)+RyjoNOe=G&e~|9ZQ@Imwkhsbz&<8jYyS50!sG8- z%zer~8f0ABzGy1d8CL2i&3jt&%>-8^buj0y)_xYfZ)_R$#*kUXY|B}fn>0lP0yNf_Lohth~K5 zb?vND-b^EF11obRZ;j~;y5Y7~g!AX3rf+mM;E%CCtzG4C-Mo&UR0fW*PYhU`mJ}!9mcnFm^o5slt~|J44nm6ixVA& zi*c74b1lC*kF6N)k=#p8*|$*Y=2A1>Lp>9~-vq51Tku}z3YCg}$+c|FN~0G-^j}(^ zd3*|zPru&sl)*b@#<9y~gQzn}aQ;Uy2Ow8IJK&7n-BdQRy9?Bg@M6CuW&e3)l3c%bh&*tU8; z2OH7A&2qLK=KWML=0rV*|J*MqJy5Lk-E;URufCs_Z?k%C@6!wI_kkAb=RDt>gbT;m zH1K8a_d>TEbD$}=%Kc8*@29rvdrt2hY=WnvpS?-69ALc=L9eNwZ|Cev;?zB|pP_YV zPC_yw+XYGU=>7u#0(?^9_UKqTDx5)Q{uoE7`G)^r7T?5Iqdc+MmZb%*+;$?j^2f4I zE~R5zc}|_^=~&5Lbcp{MGSc%V>!~syyeB`||A7J9G_dN z?cdTwbKFefRaeN1m7?+17FJ*eMWmZehtC=SmlO<)? zWB$5|fYq5RORHN;msS^)IBgwVQgR&vnMtar)EKEz*0UQT(%GgA>3X)=8P@+I%epMnR#3KW;X8~ z%VrM*$!t&`Yp(TlnEX4LdL{EZ2uG&>&YQpWK6?*0a=PC^W-@)nW5J*KehT?FJb!M2 zb9(d92l9W-j)b=zXRI5a4nDsKF^Ik?%(b-%_6R!W-LC1mbE-KX`ndcXE-dmgd63^G z=25FyN+fTnHePxwd>2m9KZu`EXClvz#J4rcZ$1q>$GPsN!JEzfQfGHgy%U{rlM7e6 zmNIiFKfAJQ+hJ%+!T+`gKc(K8I4cEj%ICW{%~{opz&Wv=8Dm&IkAqkH3{v>~u(>mX z@wqhaq->wdm%k@=@(J%_aX8;ezHZqkejc6SoTsz+Ja@vU1U#6*`+)}FO_+1JIyr;Q zgB8qu;*rsoaRekrw|%Cle0UaE`m7iP+o`dsIrC-*q36ZCZ>v=ky=#mptjbL(m% ze;XfhE4qA6b?eZa>O$Tfz%@GEyDqG5*1Cw;RD5X9_Ou<<`4wv){w$XLeDh0@)66!= zCe*uje~KP{9s0jl+tkas1e24JXW@uc~avA4RAdz=c~8>+YWow~bv zJ(#!XPfyJft(zQ|jfCVqX(mlTB1$*7hDxZGLOC*3$o$OaFR&8|KUe z>XP2pyqq+4O!76=O&{fd4cm4}B;0lgJ2}oh-8VjY+Os6$Z0q5^*LQ!yoTffCes$X5 z-Pf2ZqTI1RbKkk+qs@EP<(7?5r&o{cp3}?`ytfKH$Ok@Mr{8=}M~)vypC+4l$5aJ# zNo}KfzmxWs_H*C6Mt=_3d1lej9_e9M&OALNUHZ4gAB|1oqdC07_Ioq=o`Q)j?=pLz z?*E-GldJvBJS(T@$jQ=?DcK)m{2+6jD_$e{uxndlW%PsQ0L9>^(pK?g8Xx&^PlcUr z-*Wpy_paxj^gy_-l|CeHZN?dUB=FN+#WRhuNVT3tgg4bMTFbsgykRT-BHLE6S;xl% z`;LlfJu6JVX)lFfruCS9K~|87=Y`v&Pc~DS>oVy5XV!w!Ihs49bCUFl-m#S8lTSx@ zV_o(-@A?@cw`iywd2iw)yGDECGv|bhh2^~%=x8V3t1A;M_YB~80SZ^#!B!VXZ-f@ z?Y%=@>v3YIqv|`+!8sE>-Ov&0b5 zbpM1H*JyFWD@OM}j*V{7yA0aZ2GLy}K=-jMbWf^q9`)~EI)}eB^W6uTHiq|J`}gUa zcH|u0Ltb2)WSx1j&CHYdUK*A#Uk>u#UX35~dy&(idAw=0Gx>h>yUyJ>%^FI6#c}l4 zi_GJ3_BkHHMt-pNsnYlG$!Jf^tIYWmKkH1850Afk%KXs8lKIz7D48F+s$@xd)wU$(xaJsG>q}Kx%~AS(3;)0~?73mB zxtcR4N}fUw=}h#OS;r}NN9|Vq349{=*B&aBFIRrjiL`ksbiBwjbW=k$ZQe_p53%;I zpe;+6=QC%f+Y?iQrf2uFCjq`cfi^YCd_}Wps zp|k)v(Jozga?#cGCv&f!f-f1J7cMDhf9R63s%;MV2P5N3Lg-nYS13EO(HWQ5QG2lT zG&yYAt6j@I;gHQGm`3ow!}yW!QTIiM+OGJm_BEX4d{KQDv9CdP)*$=5v}PSZ*CtsH zNv>qmbtncyxhl%rcX~vPk9aTf?x&(NIJYvq?JeFX0-vXF?v4Bc^1;FTP{~`Y^F3V3 zXL<=aVIIW}YwJyHvo>-Eb;ZeHYrMCd{Yasu3EDqJ*IIMIKXOW(Nujj|nG-_f*UbJq;u^fN*KzR661#SW&QY)CuROzF z#pvz5soax{_x*mTo*cu;H?x1&do~kV#6P_cJ85hbbOC(54!+(SuI#;UoOI~Sl5Y07 z&aSL7ISJ7A+6&;b{c(4S-%X4q*(8m6xBZ9T{M-13#85Z;%VUn^cNl(4wg-p3<6di* zGU7b5dc=E`PcY6@M>loMQC>)eAJWY*b-JsG9W5cMtLtmXpb z!0TVTR+^s5&!H~Plb+S{4r6+EWNO>M^#`7Sol`DH22O>q44%xkh6CDKmeJOOK3UFe zYxh5$H+;REw*5;SbN>=)@@%#s8|@v&GUweQ_pzP<>?5a-cNk(H4Pqb7!R|SX%{eD- zb8u(JqQ;LjXO;8lv$|iVd4zMDvEg*C!Tf~t=$x8(uk_7tFkWJD&BeKGg~)sjI<%zv z2c_?$*Vl*g+WrdJ(6i&{^ZDGLkei&cBs8JzKX835WwqZ_x?XdfVh{t7arhF)L1!+y zAAOd$*Q_HHzmgp#_&(sDLYKXVKe>i?x`+tE_Oo1dg8s21b+C0`@4+aW$y28=AU@z(IQ~dyvXTU zOAJu$KY&l;A!rRh5}F)=jw0-2(G`ZSDA&Zpdc)AAI?Zp|5SZeM_BMUD&iDuGEEm$rpbZ`HI3b;VmO}uR7<0TXxqHWKnWlh^<-lh?SW_ zU@O4G*=BjI#vbFmBVc}lZ6`U^+VAVwW^+uutGTol`>ANMGi4q)W`C1&J7kM;=8wv} zM;Y0jvVoA#$tBfaFRjJiTzXxsWHz?w8QP_@>NI}Bz^RRe)fN~VBQlc?+XnLf?YukEkiYB#}x@4!V zcknlJUE#KCJ!cd&@w<~x1!tyd9H}2U!z{1phr`Fz5n#-i`k^PcjIT$0zumDm$2ZoN zy1!L6HP&t(qYQWZWZ08u##(9&KH`(-WUxHnJqCZ)m0HV1-nw#Yxtjlpba(#4m7|;7 zkWtspnQKk)Upm6ZxfJWMYw;pv<~Mnl?4`td_?u+A_cxh)_IHK--3OePyWV)MbF`m5 z<*WyW`mxswl+)hn3=I-%>96Je^POcwTEppEe)Yl5(9mFK`H=jp2jyeq{t))I))?sj z{OMbr$@j4q+J&t@!2O@oh5_KowHk^OdpJ=mA8XX?f3S46_r64Iq3_$Gsog!7H@)Vn zzz2|Nk0(E4*8tWYpV>CZ-bK$I_iX;;Ea&;Y+MT5-{xsuvs!a?z$-eJ+e8O+Hx*vM_ z;EVVX<{gU1Ynyu4vqsv3{#B009@a>eYY(I6BKyi%Bb7Sb;8Vp%>!R?)P@c~8Q~t>A zheO+v=*&+d<)rg-#s<&(!6!cIp5#<|+Xx-f$Tg1cQ?BtMmzKqRPXKQOT24TV&Ne&%EeU859hz(V!6m$+ zAuL$FPi%MVZnAZYRtFr%;YlaHO3A}m*1yrz+Gti=0knF3;q6(#kMsQWPG|C$h&lbP zS^v!_fwrTcAQShoPLd2srX)vep-uBt(B}>0v=5rpR|ClC06u^E6nQNh()wKA@nrIe z0c6z5ob}}kzvQzV-0JITluyYh^-4x(@Z5>a$O{n%I02uO-@-cYX#XC??^Yx7ercQfx)@(awl+2etQEWAkJ{+> zeGK~gKh;im{VlnZPU<-BOm0M%o$quW&D7Z`-#9UZPqy?wd(P)050Z(@dn2ur{PBj& zxy9;Q8-GyT_i47Bmsxk6GJuP4Kiyc{RYvQqCEjilaJC106zT=WMPeks4nMca>0i+9D(G`WDG zk?4FAKkF5s)8PJOAvi0X^P3>VITy3Hfdn^q=yo zJFqc|uy;6@Z(kf6L$=OzK8zn@^Av@mcXeaska$fJ5yTF;>o#a^Qe9^PmuArv>zp)yuN&)A!4L@ChFc zjlTQcSEp&~@ZI&(yk8_Gd~B7zd-M4J;YeWp2;!qCZ8G!nRl?8 z91CMNwU=rvC*1ismv;;dOf@!5=_j@Ay@R#)mKKo9YH}y0klRZPN$`E-DGp#GhI!{= z*$$g0F?dskZ5Uvu_qleu>;q3vgg)cxiEDZyf8^>3_>^fY4ASSCy9R$x_1s+QU>A4% zp`9}aIeSd>IMw$K4HlZ`L(VhK>OuHua_P8do^f3O%;`Nkr|jO+GpEhI|I*Wk6c@dD zXpsBB4-M3Ur^)`FafW&LxZW>$b4mx>cMZ;B?K!ijo!F_Lhkp*8-dGb{oMqhwb%vW^P7agKg$FwfRI9Vr{~>^-kP_lp*2 z50i&D@S5r%uO_pfTz&4ZtIyTv!}=+u&#&oecKbRX!}9RW_vDKgm`BeR3N8P>z3LaEivqqSC%_$mIIyY=O&( z`(%?D|NTro>DQ5&_PcBb}VW&I%AOcP^ERo)Sk|7SpC%H+CP3>_MpafkvlHXM}w?SXVx)($!9{J z$~S=wq+by`0SW-#V*9Zr*168l7pwv$}Y%^hW6aG;%qV+|p&vLY%wlf|y&(x^xKEH!)D>+-gW;*@{XFrGzhxMe|9)i!J zy>JKqAL2Z`HM6(yJHFr8Qjt#0V~ctVv3VlR_IDUMBlYg@N;77A)$RiL)kOQ0<0>5+ zhF^^5q0n}#i^GR4tjDYLTxQQ5`47;ib-Q@f!PwWjJ#+kIuGziuf!n<+x+kEM4TIi=QtmeyP~ux1H7AZ^attK=YC6cyA$}Dr9a>nZ2%nuf^!b zJ=|}iPv+3ZLi(e^>ks@Nw<9ZaiK#a0d#iHM;zJSSO?xNj0?T!8LAS=H;-Geouh{iX zZeRG<3Y+~Mwo)<#>Wj`-LKUWba7M-2cQMAf=^mcuhIlMELm=*Sj ze7wqo3G(qO52l!p{Hlk3-^S63pj&a{3fjc;{HB2WTD$N0=5YAaz!$*-l`X8kgFH9( z1vAHolZ@Ry_8OMK&+3*T;$=gTZm#3$>mua2IBD826ipBlCZ4tzd~+19UWIQ39j@Q8 z0dOr0@%~23cg3~6-<_rpLZ%No@Avw^T=y!*JoiJ}A4SB_#G9hBkd2Q+S5e&k4NXOk z`#VAWY{2Ykpzm^to#dMQB)voT>7TnXM(7Dc&r9G}3_A=xso!opTHSVtzByY%%261K z?6dUENtkOx-yG=US+D3*yfFe@#n7dGjX;+_<}SL5xbM*=nmSyXMv^1X-ynM-G)Zd) z?1zP#^U{8hrT{-kx910OeDf_ncRSxk;Y*IO3%xP=KT99p0*^f1cQLwf4ZOM>UHA^; zQF)HaK?ymb)q}*+PNS=8;AK$_Ikj=;(GB=TD_3b8@?EAFvd!Do+M-HwP2TPa%LU%S7F+f{YDzloDo4Y&#rUR=*cpO>6g`GiMC2rX!wFDtjP~ zZ@rJTn_?Q0!_-=lv+ecVzkW+nkNk0V4K$-|5Zf?q^G)yReB6$!F8JxK8wRF0&%cE& zHj8`3ky&{!vR}r^c+Bh#bhzI^h>Ms}=@NV>{3egb>5&~UMYfX3e{k9c*{>?tt8|a= z7$3!=_7FevbbBRw>JRW+t35GdDSd_FhuuSy0Pi{IQ2B0WR^BY|&UR+ny%rI@D>qR( zoBADeb!%i+-R{V&wmqMmH)WJ(r#e0RWYF-hpj0sPu}y!AcJ&w*Yb2e?rL&gOIfnAp`|Enx*v58lvw3qf4ZL`s^L@L2VjuIrbi#bv z9;Y2&G(1elzSNEc?e_TS;fVtK7Io)*&*Hhq#S_%Fk#bf&c_}ddM$bOKux$hQ%Dx0I z?}Pu!-R%2`-4i~}r8|~Ex7yNC;l_BdMMtw2v&DEu(%Ek#i%HHTa@hN={lSBbiG%pH zW|I^2tYUzXT(bw{V4U|3L(kRFcn9--Kl(^A^ANHb{O!8PY%={*$X?B`V( zo|7}4#~Dr&YB-N+et&4nd%TMtdY;cMK=!G3JZG&a7j7Qw za(yFP4Np0_bsf~J{n;nnHe62|9;m*f^t=->IYo>S&OESrNRDrOhPd| zCwp_!0CSYyd87Ps*txC1NPRF`JWamIyI9vA#~yD&CuZ|QZliBJKcsY9 zf<4+pwe9S8+KD^(bH)c5XI&||8Z`S@3rpq4ajJLXyDJ>(V}G~x1Ik}($6VzfTb8oI{dxJ~sLKhRFs547_FKhT~_@RJ>O>nx^D=~?;7{d2xor<2WxdI@#D)}+3R<~oTB+hbBf<5*yz&}od+y`&${&vGRMh3 zbk|7hLtkvt&zF&8ENE>=uDLlQpdFb`Qm<3JqqGA*RRQ1fT{jXV_k7o0pJU7CCu?`~ zb}*lKKJI?n*x}<%65e!pym8}SDLL@Pzxq6XDe&c z414pDug)^BKYVp|r1~S$)7)+Qqsr}%4$~iBEVaH@-YLX>0f#*FAYJU~0zZu-^~qTF z1!?`R@h`jPlSs5@7kc2k{&+;8u>p*&Q#PIW0`yMDCisOJ_xGj7eUcc0&c#foVi5QB zCbEp5Fuq^u55H|xcGZP)+(EvEXCEET;G4?h?|t6r8aMWk{9;h-;l5PtA$dXUfoBzx z$NSva!+j&f9)Lv#6^BTkL603bzk53mdvSt$)3#rLzb!dxoZveD-UOrnHX@G$)bs0( z6RgaTd%t{%ztj0F%Yd;u@;=TxnBCS*oWQ|nFi0OMZV(|pp!;Q%(>$kmK{i>~gRPe- z3w`+dJe`)qxILwD%UJVd!LyA$ouRnn0Qx|_PtX2t3eeHr=z~n3`e14uD4(bHq|7w^ zAlul_2mfzx3>eufwEC2M<;kvYSlmkM#TY z|0Lb`NI&bQh?{$mj*+lG_n_5--W+V|cjtX_PVF4fl%5a$I^V0e(AB^GdK0ex_1Aka zrGKaP6lK)gVft@u`uAs9=0(43jMRr?i7`kQ>pbe8ql@oD7vJUA#q=xdF{6t;+5dCN zewNs92Rhm7`@WQ3&K?^+&H44}2ftp92k7NSpI)xa;HSSIAAvSAMqK@z2-MH%*l^+k z{miop$@0a;h7&&h+?>+Smm9|&ws*|v=e6Sf@n64w{%z{<%UIUmtU1Jg`ueEV&m;6T z`G^sA$+M4qdZ8QIF3`cL@hKgwb?xYJ z&C&E~kgeqB+eqV7=eWs-BE9P6uIpT-@$6C2x+Ulwx1g9}rmmJfoWK@#eV5h-_USyu zA;S30D)1u}umCQ_0X-&O{*cWq} zJYo6r6U=dg=016x*vQ&@+{AO`9#yah`3$++{2lP?eZ$Sr^AveTdYAV*?AiT0Cq!OSWGD)b zpOJ^9H9I+xLy855ikwG9xBSwYUl0B!`*(FsTxMDw?|5c^=(B!*6VJ|&>w7j*iM&+| zz2lw-?&E*W9gMIlzT%CIb9*{i7$FLpVOIoUOLbj9@6{};-34w z)bk7I+RvNr4BjN*&CkQU3&n>|d!}V?f^$rJs8Om;oR32p`N%j6rblPSsBh!DL-hsN zbYEi6^bBOcXoUg*R5-22EEFqUlN(o){X2m&{n5X_QicE8~S*FZSJ`180(Xq z*4F5=%TMF$RPJ*Ud2(rZ{uA;~w8wgVt6NA z<;i5)KbzUE)Vkd_R?6IY$!k~IX4ITCyj=qyy8EdwW6XSV#btMf*&89*Ekkz8>CZ1C zyV~dP$?hzl>`wE^!gc(<3)%Jd{Fkw=KJSy=y=H8X$c@bJm>JS)S|# z$vNkUT(k|6P0rUzrd&B!d%STena@X-)<^Q&lF0d!oP%KHA#JA|3Ro)+xNAi#4^!L9 zQ}e0Kjq$(hV*sD<$H<7}+;3xij2&|zJR^0sKxW*)(y=Qw-bREAN9pZnm8#_&atO`lA7^7t=p9?P_O{P|(Sax?IgC-9#(GUrF(#hisU=VCXu z!y$AW^S$ERrCQs50lD0*J-WzG3;T?_I1_RXXX}%*QOeo4_060K>A}mco+Ehn@E%4c zQ}CR%FZh;SQ|h}o6Vk1Z>zw*74=3mITbvW?o4MCaJq6SwI=a9snBh2ksR!q zDSU>WjIu41?Ic&klZ!tl9;>+NSaUyu{KA6gg4uQ&j!?zmTk4^J|h7@$yQ_%ddDB+vVg_cxS$6uJ&t$GEy?jG& z-1z54*>@KMKhl`XXdX77?6LC+IW#7Ba*mrjxd+;%5AQE@R^O4z6`DJAe+Ad% zRN362xkE+O%_eUWUC&xNou^mr%5{*gcF=FEuZiExn&Pm}$)n$+IsJSl&tn%n5At`& zZxYlO_)$u_<@Xq+KMuL_=kJe!fc_{*tzpysp?wQ+r|&hb#~tDgj&n^si{CblWx8 z-!4}_d;2Xt-QnMFDczyCRfiL#JJNZEnSFDyed5um@r(SFV4ELJ@1Nj&saLEWB)f0h z`)Rwy57Yc(3v&dR4L+F6u`p@RCHVb1eSSad-^1S*B8RT*=Vhz|^3iWebX^(i(EJd2ry% z#H;bIiAV9@5|8j>bImz=dH5oS63l5l<9RuIX>{Iw`H;??lMV^^v{I74XJIOXLwmu|pr*4q0F&$a(rxlGSYVoxCDK0;e*Zb`&~HDS=p-+DXkadRSQ|LU8~sLp7ych-*m;HRjyXieIl{L-PrBdU z^_BQuh(q}N_Ru!+j~bMtI2frKI@?)Ad*%C;|E6MS9X$Fj`#wL<^S`^wo<;msuC?a! z=1rX+u*+TRcH=`69^p`Vt)cvI_z3n(w)%j33zz|51b>=(X``XRD~rCksbW&!?^f&z z*H`2^YvhkU!hBWb$Tpm2d`!EbPiK+nIvf4FBO&8QQ{VjpSaU`udI>po%9Vc$ zIerQmUt0H6>D%U9uAH{NLU^;l;<#UeDI%Kt-&%=EDb)65} zv%TcQ9uMC0Vf+U0uAFP>hw~L>oWsdD=Toq->j}Lf z&3V=4w4Pw@kS!5~rbfzDyt($<;?H6BT}z%zgbO<<#D4Dr_Ivlw?VnM=esAR}M~Dra zWxsbbZD}?-*^H&<%fJzK5`QW@!f_oq%A(HSIJ?TeUA`;9vykZzJyVXeg(#CaO8dn{N{A!;`GF zX7~Z);6LAa>G2iYe;IwC{N%s1ebDUd13R9gdFsC?`L=;$Dqw9{=8OG?-E~igZ*8e| zdSgobxRf?|JhpcJa2`uG{X8yD*@MI9^~>R--{$rI&a`>_em{+&caUG%x@RLcZ+?*P zJ^0qEIA?PYa-9>t-<*$G;pF7WXH>E3+WPei#u+`mm-$^Xr)$PXgRV7}nMZj~MXXLb z`&sgcU))QH5#4Oi?n;fzR#W&G$`RjRal|8S;)?x7f%;MkY;;#UI416*0=`3coXW1&wm*!rW zdwZO)xwq8TFJ<$kU%QHU-u1DqbF*9L1C#GJ z=h8nzymcIT(u%vDa-wa|LiPaH+#F**#NDuG5o|YSP zR*#34chr8JFUkH5(aSuZFZwtWqG4Eh==93}73KREbH@6Py=E_8A>~@ZGY&iTZTuIU zIeyJ4hxcJn=QH24w7uwzYb!VXWZU9Dm$IwEwtL_mD}ygj=RE6m)9Z8K+evUfgRR*N zAH&e5-1Gux9CGb!d!Iec6^^s!De90s?N&ej;(WvRfIF4EuwOh{F126o^ZMmj_=0{p zb8Wg`e&x2~`oqEfQpo*_?Uxo`zYHrM)GsfL-Y<9j*Rl7@y%*UpH?WRTzqC?^zh4xe z>i+H@tx2ALaeE(o77qT|AFU}5b#I@Iyfx7d^^4~7vSZ0L-7d`R2d5buMbC$~Cp^s`d-F}XOe)wAWZr}Rf2TT|}X`lR24^J>*syf0nu!E`yr>=V~L zlzz6rG4U6bEA#0I=eLt<`e+9-pNhqeMTf|?k`8%?F`3Q-(i%kiS^E%dsrXG&HC7$ClF_ST)^7+X7&VV?&Mu6ZHKVg_3o2KU+j8dmYqA5e_(Bt zX>A3whTAA-eCuLtLo75po$ygUc3k{m*zs$Gj(VAKG>i2rq9$~{< z<+Tl88*hM@`F!Gh7VzQRICSWA6U$%4Z`r59c`Lu;=f}3rU|r_y;`2J6cll_|;mJ>h zv2TaVPjZ-jJFRVyvFEpMJ=sC0Luxv&;!CI2ep`0F+HTj-*QMkM4g)_YK_oKb38-dsvc~y zm4*AgKNZh(&ug=_H;1=*xV<^eyyMv4X3wUNyD{}lpPd)m$b5gDpFjTJo~>fnYtfSA zXUVmm#kj-H2|;TCd*Kr9KDitxQrC^0qIYd+ZPYb{=4bOKoy@&jHyt( z4_)2&8hw!eE_Q*={BGi0sEyb^23Kqb=fj7$C8zK{5Eu3jfql=|BD_03a7^_Eg+^+kT<51KcA=9JkT;cbJQ;S;Cs3Ey-k*Xn&T zU$E==cWI;e@e_P=vpLKDInLmmr}Mji8QxaSZ~3=M1kd#=taoy+n>}Ur3z2Q?ITD=V z;{<+dot=qmIrF2zMg6B)1HFrFBOY}!F4R}@wXLRHCcntJX}E>^9-ovymEu!Be7Y7s zG5&)1#K3aq`92IlXI_73GHskPyC2<5Ig(3KZJNh*?pGZ9e7v*mS!_42U3!m;*1wtU zdKmgmyS@_MhJ2*@R(sKc+K9~LZ$Kt;k-uDIVs;3bP`NTIKU!0~i_B*7M&mP+H;t4V z$NA~BF+Y^B<(WfE3h;lCv)RLba-G*q{wDrc-@eCp>EBbF``%6YSFkIwi>JJc{k(xW zOtvw6GxqQv+N`lMp8cN4a4$6X&I{$1%EIh2KNwXD{ch7Q`Zob}@A$VBsgG~1r*Q+rNxcqUt_ z8a|wG!g+bsEv087c|Bie{cwhUSs#kDJ^WivLo>QaXNEL~BD_DD_rdetXq|^#NT0k% zU5uMa8`u7Tb4JGR(|O4Ik?mFJp*|+b1FUVVFUosg*TkN#$#%Vw|CU{E2xcC*Jf7J- zuxYpnE}UejiT9l4@Yxf}t+Uq!y~mMZy_aopp)0r`yn3yIno~6Z#*$ea-tuUh>*LKyPVp^lm4z?J&Akdb&;5 z=;>UZ)uN}@qo)tj-@sk7n{}l0SqX5NxTMbvF6ytO{=mMNY5SsMw7ziR?snnEUt(WW zA{VcagMNUqAz2#VF|sTrm?Hw|zRuEJ@6uD_B&V$B{TlDlW^WvHxiEj>uK!f0#+>(D zzJxOo#)%78=HkkwZ87BRP-?71Qe#DOR*_|lRHN(r=!Y@M8}r?6cfJdfH}v)e^Idaa zXtL?gKIS{@5$SeMHs*0%%Y0{S0L^#UASwCax90H+t247itTOq%%Sl?V@`{si) zc6E{6?BzWGgIi z$Ct*G;JkUq59iH4-nsql{ZEMwv>Z%3}oS;j*jU6=>zt(*O48J~FD zj*&}jpRqs2+CG2VbVm85p3~ev#i%{-<$k_N_Ks{Ur~Ol;#i)(=y1fvo{nYsp;JlJlxJ?3`D9{m!SL zE_q}f)Zf1=G?VvI))fF(z`MXJs9)oz02{LjA6R)c*+bRwod!Q~!U@c29pLXKrci)i zvZBwK+zQ;G>Vi_`DVS%}ZJvQ|Dfqnjg@tR{3G7b6&87SxwBs-0j2thf7VYU`>_(v> z#&{~H|2vM`b8&)l=dQ>Y<^1|nwC#fP#;@QQ{mdBU_&>Y$^Cgbaoq;+iTl*GVIUV$$ z+&pi8A9;Q1*FlFbuYJKfC`xtL!@!#D=s+R4&4q|8#)(KK*X4bJ_~~_Hgc#jWg+d z%^yNTrhjcLGHT->n4}PqbJCx7o-n@_?5J4|6Z@{S7AS0 zO1sArPtbWTJ2mH2+BxSg^yNR)a;6<;$D4QcVo&#(eARn;l*y5qCU!a?#DZC_cWkiyzsg8sBHP(es#oU; zX^)Cm{|ml4HNPsi^~nD7kA3W;F$67oZ$u!UeEjifD)ZS#-o3Nb+vJqJkPLVx9_jf{ zp{pruVG?2I=RxiD@H9K+y<@@E9JX-adm#v4X5Bvc!1^=cMZ-wAnF~_w67H68MQ`^g zWfYU~c;S_4C70v&a8>WXC}nkybGEW~gqIF&3@;n{dia*!ws3VX@A3N^-YGcrlTN4i z{Ya?S332X6r}OAY?a^942!G!wbdA7=Kt8J;$;%z|bBj~a+sYoAGwxnHZ(hJ<{ORAP z9Qc$2pK{<+4t&aiPdV@@2R`M%ryTf{1D|qW7zc1Ln7`1yYnYnGot*u#^4N;p*vfI`vE>tDw~jB5)lQ7n<(0>(ua4D_<<6hzJi+I6 z51(%8&qrrDbPDbRKF3_RZho6TADr4##m#(ZTJ!}=-*YaQA0KeEXMY6Za$+l?ad~d+ zR%oo95UYd6>WQ(MJk_VYtB<>Q$p5r|zWPL$>gxh7>B4OdfKz?3717vAYFwTZyOkPi zb7OVXSUn+DLpBe8>-fA1o@ZS=Z;%Xb{(SY_0-h(MDi2R`A#XOkbv zLlW%Ng|95HSHRzh&`QcDlH z*Ma3z{pmhdzdcO3{9A0f%hd~2;CmB%uekn&H0udCYfLwn%OUq(MX9{paV{JS}i#mc{vyC03PVnS>sI%xUC z*sYA}+N)!AjOyx1u^Lw&s68Kpx4XvTts?l4iPzJYvv_uFrG+DY7YHXl3&+vhEL=AI zKN_D+|AXg*i)X;C&d=wI)dO#Wui>kfuD;8}r*%g~m?d8dZ@EifphAV*Q z1Muv;OgzQVvxq78K^IS)YxC#hyElH;0hb&DTo-U2U2zwkpYOsaeZamQXRC|vg8(>< zkBij@>h~y{P@-S6{dBo{SMx^$_)fU^9!J-hKOcXx+ac&sfcH~=*7_?$kL2ep_}+B! z$xbqVK6=Er(e#t>EFgjI!z+NN9X!#5ZQrHNOQPo(cow*LF11|?z!RP5IAt!LYm-MZfUj<}^e3~-?@vU(A1Qx4=XuKYU__A@K3m>65hthD^<*saV;wUc6X z*qYT-Vl@Pl!TT_Hw>De6uVm3@Ped;UtF=eYf_I?B;w{dC_wi_Y7KIc;&n%W0ueM&< zc4~aIgZJY*EZ%cj=*ebZso#%M{)ZcF`J*CMe;M*T+WJIz&Vgsv*DoJW6&rycyi7c% z|H1RIi|0A!HuIO!u8Y-gYUf$-R&TfM-0z~z)?CkPFsIAJKyv_a6fe6o)afxi{dC-C8o5Ou~`7u zCuSLk7mHVN-vZuOzG?Ah;n%4CB;k1uJf**X`FK7C&$Cwm&*FT?nZEn-=n>yJm)&{L z#q$Q&=I;vh=bPZoOwxQa;pF2BfV*fpT>z4! z;C;2jw(Ey3+Kh4H`A^M!bQbvKdn|n4DDaDTp3lJ=Z@Td8CANRV+s8U%xV@xu%PIG2 zr>#e4Cz!tr%K7~j_MC+-$~k*&xvg%o%=U~nKdb&%!86arb0TZ~7t$T&uz;iY+xoN3 zODX;B(x-l3Ot~Sq+^j5qvJ3QuTV8Usi}J5OWa}+;?`O2z_|uL6ch-eln5CZKb`i9J z?@h|R`LMjZ^xYmt|dfvHd2z zi$6=d{@mhyILmmrSbnVo?|$$e>$P~R0`&dm$b;4wAA`U9|FZa3W#MybzJtZ1`HibM z6sP+yt_(i$b-(bvXz^VsITYRxz#H$gc(0T_Rt4TFLgvf+E#6Dj`x>VYgQwlalh3vJ z^XV6jQ)B-DmvG^#1K>1Hy>*q~&jP>Gg@1e$c+rs`XMT0z7Y4%{eZCmDcgFzN0^EnA zz%8Plhk+Y%;qtS{Wu{&h|4&fvbq*@h-+r#mpO0^2@hfOtOe{b?PFOtM7RxX_DLiA% zOVBu82fnQ?J~qbMzYMy9=gZOOG4<0i@V|1>(yRK+-*EiKemo1@ITx-f6bP4&ue*NyL( zcmLA1zaZ~pa+-$9G_V|)98dEjF4s{b~Dcfo0kmnEqE z^NkO$|Mmkn%Z2+e0PbS?PkcWMzGE)F4=xj*+OY`XTYJW~BR>G2+99}gz*V_$6GwpS z0&boQmyIuy!{>mT?!v7Nz$dPP_p-zQDpu517B<^qBe=0k_kI%O=-V6kZ41Iu|ZmUd34Kjw9eH zF0^!IlLy^@6}W{iTy7Tqn#oVm8C}4?B9~A6G=Ia}J?gj;|CWR2oLhf3{%O3l12@mD zZ~qwR97$`d-V@;4RBY*=WQgaVkA6=+KLD=5g{uyL(|GanlmT^S{YDyZmiDC9S>pe4 z;2&}ED~H(p`RK5Ed$|8w<+@bQ983R$ZZSXoDwonn?sK*K80C(+<>m#9AFthK1$TqR zpU6U&*KXm>zmc`_0t+9c&piFU7`R=(Vd0Jh({09i3vfd&-0Q(`f$e*ea{F(z_=DmQ zUi(f6?(-IIw0>3lJ_i0d7d{a{kJr9gpJ(oL;kE|w&4a4~ZowC9{Xz4IN9QKsUiaVv z@cHFWbUq1u`xh@vUx>|zrdVvv!$;%OZzhQt@^c{azk!8NV9(d{q-bp&Tm=#Bf*^jF4u(<-^^bI zKFP%gz!kf2@htPZV?a7uG>6iPF z>=eoMCh%=qY4K%?pLu>;@!?6}KU`(u<6N6RUq5JyTbid1^!pT-@Vr2&muRY z&Rc3v{%Ed0opb4W zBLJWH<{vMw0)NECTOB;V8NK&0a3_Ax()ma*UX!ml>r2>m>n&VwFr2BMj>_se7GGMx z^Q4RCuY>WJ@$oQlRS8?4_I{Z^pWI!JJ`)`ufIr!6@n?yD8UGl64 zkEHh`UoGJK@OFzY+j?&_IhFo+4m^uDSUj%=@JI4+sqqx)`-%SwKTey)ed3=)Kr+uW@k{yqmUKyrZpm#Q(Fvce(J{#?ffwTl`82r!PWASI3Z@uxZb{zx$hxc0eqoPHBK6*U;FxvQxeuaLz&*A~4{Tq(Q z?tcYtp$C^mKK%5p1Ag%?i}zCXhvf1Ic%F0d1kJBjF2}Y%R($&yd~bepWWJ3i59+VQ zzfE5K?^`_kE!P71uSdmvHUa;f3m?}_{rULp%@6y5J2DEK>3`s!bm3mN*oNV={XbIg zGiOmQx&(jJZrctgOTVW4FQ6MKA3}6BfbR_#-@ql(B|J}pXKT{Zl})}!>nHL3EO_2@ z@$3wy$IL$y%NXMww!SMpzD36-@b^7r@%IMv*~rO$;8yLka8*$X>dz-9^s9k8RtC<0 zW8t#sL6e7j7PyC9xOgzWOUi3nTuxtq$JT!~K)-wP+ydOB$K3X)E&2;+&%?kiaN*(x zXgHjQ?HA>M!g!x(F(@pA6xncT(AuKQg)W$7XiQSfQ5f^4Dc<$ z=XP88cSCN0;dBhczaRL%gBE^Z4ERp~zxB}A;XejGet2y3OKyuRSzEa9Rl)QadvQ5% z@49f=^x;^>>wfSoI%4U2a)kPh0r#v6w=NhCZ8h?_5e<}!(x_uDo-yY!gLx$XnE14g zf5~Uuei@?iv3T3LHh&k$4FLHx@(0e(?RZi!a|L%Re4HJ#Y-TV=mmREN~jHXMy{n3#V)I=W8eU7=QM*>d#+> zK5*gV_VzG+rTT?uF>ps*xP`%RDZGN;bdm5+`rt>SThCtwzT2f|(HQ7C=fl5v4DbtX zN%Ju=2KaTr54iM{jsgB*;NKl1{pzPtu67y%B}4i zw`~6Mm+#zs_x8rE^X4bwTQToczM9{<=UsoseQs&`ywIkeY23gb(`FC; zS{L5CCJo#XPVaiA`+U>J&23+Bo4DhiZ|E`w^EqJDmtL7TznNy8alU&6{Pxp5higCG zbKU1cEZ4V(i*URnK6%%Ka}93qT7B%tt1;)tt8jsyH*acf+#J7it5;4q2?3_?YHawg zaSINs7dP6lYnG4ZET}toG~Utr<#>GYj^^!+?adp~{r}q&s>72E4_5LmK8vn)e(O)) zl0osvlS3^Z^-;f19+4maVOuRgDB})5a!*3b>cRC;UZx)Q4&C z+c$1*iTkKy`~fQ%)14B&Mk9VH95?c;j*seY+_9r|yM{#EV*GMEF>h1r?d|d3j&I+1 z$AoYx-0b8i1_+2JEK^<4e-S>bG1e|w9@&-y!B+g(~c9JqL#5Avt-P(FzM*{i()u-()%B}tRxMq#a#{VF@)fr%UA1Oq!^(!Q zt+GE>tgOFn)vA?i8q9400($Gp-&wJAP1Vx!s+C_|x@OsOF4xwqq^^dq@s9%fzp}E9 zuQf|61tSW$T6*g$15#D9hO5ezD^`I2YxVqFRk>oNsbb}-h7~Jst-pnTOK)AVa(T@P z&@30OrE8Z0v~&f}Y0HY06}K&`U$LwP#7i4)Td`(od1W=f)-0`G+Q4@eM6D>lb!o%O zmCIMIxaHQ{mN(Q?uUrM@`kLQedaLl>s=~`xu3S}94?#8cqNiqM&5Ey<^R!`2%?b#* z1$^~2%j=h}uBlpcYxT-{uc~DgHMcA)2kX+cHT4Zut8ZILmA6(cg{xInYt}4XE%q$0 z6HY^t{juz}`lVI+SzZB8`B`1FysEN%Mdi}vORFkwtN&U>c@-q`e_8o*npE|*@>|Ml zR+KMG^@Fg(yYjD=*DTlXniZfdudG~Je-jkd-=<&nOLw-nZr-7(Azs4CgI(w%@p>l_7RlYs-x*w#0Ybi3-`eeM>WY>97b+w%Z~! zbIYAO;tY+}?dT)Wd{xt9niwgoCe3PFw$s#&D6Pi!Mnh7nsV=V|u(73OyKQviRv55# z`$o;CHJ@J@Z{D)GeLF2e?^#MH?IV6coYJXhhRRgKG%jYCxH+x7-EZ zrcJFkR(VsmdD8lgt?l=isv6TuU3G!wo^Ld5*|f3Q^s!p$gSfF_15GyEj&IyyFwp?@ zIt*HN=Vsgc!`ZKn|9^}7{`jb>Yv1#0eq@qBg8T>&aKI=LLNaG&GBXLFpn#wVD4-~4 z5|W9DBr`EHfuy1x5G$>ySZ$?hTWqQIR~36{rHV>hw6wK~y;f;U+t}iLynB5XZ>{B4 ztM9wcUOSnYJ-PS2cmH^s&*9Aep0)PbYwfkyUVEL)nVuH18S<>?vrx96CaeHX zKS9o}$)9ZZr*S_xQ}i9H1BTn_ay*gjyRJjDO3Lm+hf-NhEb5Q9_NA#cVNNiS?v}K4 zacqZ<-9q4@kcL`vER_3)>IPUphF^4lxQnY{klif_VXUwbTC!jODPgtC`0IW0>zo9RlXpPq{Z9_Ai?VV6}Z@e2;+g7_InM#wD zWh;_YlO@&#Ep0<{%elm%=%olt;~4YKR!GHtBzHt|wwsn>gd9FD&3h7j)12s zdT~P^nH_ndWM41rw+&8;OwGSJ+1CZ{*4oArKxnUOt;R9r!LnAOG zQD0)nsPi0+5OD*ETf0CwFT@?)o$%1Pvs+%wox~EwU7>;6mE>4cw7@`#338?jo9ERP>W+=Lb;@U|0-uz`DqISXW8A;6(<|RL?yMg_jPp*)H*Shj5BFpOfeJ1?G$TC z{7iiIU02$$Gcrbq4$rr~6Q$SeRn{ z|C@an#dH)+Q~zmSklhx=o!NDY?Pc2-Tr8UTd%T3dc~Nh#+~X0ij0GtkrZ`mAX^%ns z3W{mz-n3sKdZ|a+TV&TMMkV>E5Dk^O-o8}2wXVgE#@Yg*V6ZhF48%hrxI;S_j@HNG zu~4*iLt8A+5^0ZwLKNutw)76vVT_Zlbrkwg-L;~r!)_)QqC z9A{bo57cQKnA!2k-7R7Yz%8c9k93mf>%;$RX#7jXB~4TFq$V@x9SyjvjuEGrFgJ9cS8ee~g1< zkoAeUNMofraS7)Brp`7@9PTzbdvw_8FZK&%WzM|F?^tKDPAq}(Hu|u+w-d6Wt1-~i zJlKna?0UHC4vDjBOGYOy0!~wbzC^sg#|bc? z6L?*?ZL9%F_;&4rXBK7*$@-nR$b#08;ljo1s=8mKjz*D4C2^#XB1?&LRtuslx@%f;k z`QL~Q<{3E4`t`6k%E55^2yvk_3euDQMnCW^;3N9GaHhSfgkrk$bcm9+)``>JO+C$?{4YWBp zvT@20&clJnjyXOLkJq&MIP!a&8c4L(B`KWgO?BWtq)Qi^W4;ev0Q>?RlIDESkk3`~ z5wz)#H?<_%n&8CX#?J9$!?=1;)1?bmUE-fN&wu$E>_L}P-b=RiDB7ZY%nEb^&It|B zhmMvM)_<|7!5Y3HPCKAvX9|hGwqf2K%uC}V+xs&x8XsBz2zAoYN{1VZO0pOCKvc8= z|D}5o=r{V3WAUTppn+{7eY7fxP_`|ODTlzbr8n+Rid|NXzqc>p-`tt*@Hgi=$a%D` zH#=Lq7C$yH85+V^tkdU;tUB2t(a7uM6RU8j(Ydcd-38=jq zlQf%;a#pfV^LILqcKE3-(h_+vVZV)jAO2Pi{iFN-4rdGpayjHQ;h(U4iYOLtQmjRY z+TbqReA+4Iag8r?a-jLP+tZThY~6?%3)`&%4cYDsI7prglKper1`_@SiGlOx_9bW? zY?E^Wmz_Rbi1R`mi*PK)K{mMr$5I?rvSW&iQKuZ-G8~uUSdL=_j+Hp5@OnFA2K+Mh z3hNg3Z!>1p`0<<&eWNixf-~i@-oly2SL#C8+=!NCHy2)7;ilR;Q$1Z+-CKrD4Z8jq z?Nk4xPIMR-#gejapiZ&oWjJKKb~!$;!a;tL`fA}=jbjatY&~3odN$3Kd3Cb!t8lPB zhLo`xTZg#ZI*^lfuGEUfgoY6PK02Yy@jJdDu9kN4f=;!uv88Ks%Rs8hS$?X|t;t~> zT02TA%AEd=aQ`8T$>v@O9*S3o*O|@)vPl$*Ik&Fi2!A{1{GeOj*X1}7(gW#k7;RF_GYtpD?wtv&qbck#D2gI-=OILVh>qgk2{?u? zVOK`^+BSrZGVJIY&;k?7oH3W}Q~XW-EvJ3QRm0;=rbqLJg#p`g&8HNFrdOv^ra4Kej#b{?(xO zgKorz^LpJ3M|%$0_f)zUaX_-Ssn>B`eZBD!TTIe3r&qb3G?vs)S*Ljx!O^?f z*|XDs7*@&_QeX((;9ALK5mVG@tHgd%TUsbNp)C|C1E*h_*kCzB)*Hu~nu2jrbgnI@ zJeEJ59?+EiuC_v^_Z&VamZL-?ctp<0*}-RLqlT__os=zL1cO_hzYXD2~r1cEV$=K(p2HWJ$Ec> zI46Y2OQu-00x2Wn$Vo^!M7)I&wl|c9AHKJfdn8txUD!vhMJPB9d5SZbBp22NO4lLoVw1w#yeRuhr)Sttw2s$V$>_FXJUJtHSBWB4bz4rbP~u}SF=$V z6=EXC>am2k!5?4NmrS?#J?oi$Z^!}Seqj5*c(k^Xzrt3Bb;^fu#tJ?;t{ zJB`Ur>#nt*Y&h5*C$(G5X6+r_W8G_AYt7Qu>J9oAwI8`2&@VI=>mOq8yAB7GbTmp#-0)?Br|mj6Yt8_si!J8z_mBYIKrkh|%cD2PP7d<9Gxn2!Y`5|Ih|b z+7X;7uEhF+KQ2g3n>TNsy6xmYXZWxGnnBb4hW2PbTUI67osKi*Q)+Q0Iht{dM6((+ zBy*#GSk``E7=oek%dx8seR%c}@1^+7iNiogHr$EhdK?>Z_)&JE4L?3pYytW%6n zviooJ)%a@s{u}+ZwZ5Bb@W1~ir-IW>)xPSQbA9WmeK|k6@c9~PP&pk+^mU^zQ_dE- ztvKi?mlfy_1Oj#-7zhQzf%-rs5Dmlv4R*k`?Vug9!*;zLv7>g(ZU_c~b}$$W1;fGm zU?dm~#)1u@K*$aSL!nSOR3D0jqM=x*Ash(X;b1rv4u|W*k#IB|3pdmU>h1bqeW*TM zUtb@oM}EA%Argq#kzgbg2}kN9kw`QWi!?+7Q9BxphN4KJM$@%EOkRJ`UKp_j9< znWeq1tkqJE(G` zd+VI|*70IwuKNGyf?obNi}9ZpGp|@wvhNs!uFe8bjtNhD;DS2q!k>x^KwEUZT0C3X zhR@_*WPJdi>3M~$-;`IUeKO5?d7b=1Ha*SJ?CWG(bbVcIZS6JAvt_m{cujt`AUrJD zR5SedxgsxKf-~8$^kEy~*CDK!JI{IAP)#4s^>6NgKPH2sEj4)Of@c(XF2HV8h4!gOvOgKLMY)TIai-kSlQ_Gx z+E(<92VC@LUv*B#-T7++NAJhMcQ*Kt3Uby%S)YVD?TaZolIjfh*VEzH?XQl;4dSeaW(qKCZ1hfBiL0-}~eCJMO&ui;q72 z>~k-@{L`QP{=_?~k;w?A6y!yrWsAqnxXaa~3UL zdhxYw@$Gj%@YSEb`dVrExr>%w*%rU!?ngoV(og?z;+<2a<%aA1n z-*W5idmnz`#g~5a>TAFG?ULQk|KjCWUt79-#g$iG+w}Q6?|S!c&7b|=OK<$@ z_^J2*+MBv7-S_3XrgLiRzWvlQFaG4UxBhVd{QGtX?mF|(4`2Os`HHL8dAy^`X4Jj+ z=R`6(@B9lE?YjGuPgh;ncjTu>UqANi|MAJEn!joKwm(?g7J4UJ?((fqjLJM-UtOFT zG$(p>tIi5r9#i+YJ>|ZYqsMsGcucF(S7drkk7;05mRK&c*sYHm=V~;|J#JT-=Sq*^ z8CSZ}y3m|$>Q=dXbV;K%$~ zD$gv}Vqc9_V(F$`Tw_gg7n_+UaB154%dFXE+B?@AZO-+?yt7=}J}s~C)|JmT{bkce zXLefK?weRVZpXc@I@cVJF{+{{^G~zWC7CxTmAEpWx-x$#d9T#meuo(?+PZ#h=72Zz z^O-Tz=Z<+7drREu;;H7<)>TEBTPiAj5>-&-=lvLCXx{^lG{iOZGwR{7TM zvi|CsZd|>?%DiAsHb<9gZe53X2F%p(xr&WZR+(OIjB%Y&K2{%ROfV*vR=TEmtMu#5 zjmAOab>oeaUlqM+yk-1GA9THKoHX9^pR!IH|6#yT^^zHL&Rf3XuCIOV>uyg`WZrpK z{N?ED*4TYC+RXpNegdCXm9Raw>EaIv1* zwKZBa-sjEiZZKQDMS=0IY35{?K0jt%;;OTJo+8hD{|u|7C}K9cCV8w9&&s9IP-)0h z>-B9t=kn#%-kDcaj-61n+?qQ2!ch}FKKByujH15c^B2r?&vE(Om$`LUnd!>hzG3PT zuP^iPwbK_B``o2xGUpTnE?ylFjUG&fowl#QWT1N9ZzQw+3*BRUPt&3k`HF(PB z(@@`c+PnSDnML3D+txE}v)uA--T8TIqpQ>`@|4}vocYj~@+KxM&fGt?9=Z`x6WN=`7Gm>@`cOhWqvf*ty^nclf%Z=Q8iXu z$rZlLld&nKHC7Q!+MT)omSf&hv(!wNT;;)(v0~_dwRigRt*cAMnWoEAG|}TJGD|(@ zW`2IQZ@YU$cg$|NiSlx^5^k&$&&=t$M>P&vzY?Em{gL&{@tK~jWXEegsMAxl<#=x7 z>{r&L+hfiLCpc;9>uE|S<4)ET(NZq&4dA5!{uaxx-R)}Dt~q0mHfF+}sU`lwsei8V z&zc$7Gb{OUbB*!HV9nHz24`!Z?5Y3Sr-SwX&|4z9@1aOlX-o9+QE$iUDyB6ADq9x+ zW$Hs0pSr$f`HJL2%fEVX+KTw=Z?DjPGi{|d`1WOiw_8@d`^Se?A3gc@YQI*y=9K>8 z8m$L^20j~6lYxKwlH$O)GCl4w+=iiBXX#TXuPbgWD$*+~y$G}1HOt&QTK7jmZh7IJ zJU*;Fjl^tuLEtkc>4wn&XJ8p{iTYH-)QhR^0)al(7!OwgYP9UtJ*Ll?s?Py$2{^0K zc!TMJAMqH)PMg#n+|ZzUrO|-C$$L!Em*|#`rgg7=nQnMWyc=|*sMvk6F&VAtdUTYI z`?-qssv^DJ(%tBiG10KhGOH9H-TG)9`ZlMSQ;ey`d_(tmb)&dQpKWe7rt6zb%P7*_ z=5HZBdg`I}46oZ~=z;R{tpMsSy}GEx@Iydk7jO~XFdMywvD?&3kp{r+OylK79odB+ zPt(jh^k%>2?ld$@_xTOH+nZdaKGAUL_ZgGMl@8b z=jkOzxCk>#Z!$@NZhen#ddE51)b;WDD39s-v6n=cK$^tp5S4EH8~WqMd9ty_OUU)4 z1ze4r7)F;?q#J+5Si=VNUAUK}`+e1JXLQ_#Sqmj=9;iXTe7xbI);{CL4WMUO7u`xn z|FJx2`uWyn^ciwmF0a?{Or>8cj#@#lK1v_&(nq6}a;F6sEY#9G-5#wubIRGs(wsqj zs|E0Y;J+v*@xTQ)8KMgFr`;Zb%Q`*Fqe8#KK=eCBRV!oG)7UH%{vg-oji1DW5yGEf zd=K+oiwD4jFQZL1$`j(GjY5KkaB7c=Z07}hAlwUIK&3~Vv;+cK!uuJQzau@mMBpna z;e#US_p!W(7?<)jv%K_MLsWK)lU63>9V-BdA7Xi{=s`A$?B^zymwt1HifsQJmbW7h ze-oQR>i>gz_~%&Oqj`9Q<+aG^qR9KcRV><}-@>6He+Rjr<=sM?7ZmwB7WBIUbl<)7 z;1WfS<1uUq2;WahC={u;9a3J}q@c+Ao+ssH{FJz?y$5|!J5{vdMoIJde~sncO%D-K z-CEQ++lud}?XYXm+hPTG8yH#}PahZsMF_Nbi? z7?t<#sw)-ja|2sa0;a|NN) z|50ozsXv){0?(k|R5C2DHs7ggN7&v1Eboy80kaG9vzD^A*OmiI8@ z2N++%@@~0Y0L_e_h99JMjx#QQpYaivch4F@2=jBwwJh&G#)tec%e(#xLD#|bFR;9a z8Q;Qqg5{lmr2sOFPnGtZhrh`3&gTgse}}M{NWGoI@(waCf9LN__+eGDi2NO}8(H4nHw$|CJ71M7@BVE9pAUaT<$0F( zgU^Y&)I&GR+jg74cZ!oXh2=fPc$o2j=Jlv*hX5!(qjC$&+jWPi%lG$%SYH2K0+;c? zpWqP49+uoK@G9=-JuGk6mjo{LL%*p^^lR@E_(^fne#Pt2{M`bO<9I8}Yuzt!xEH6? zv%D3IA7lI-L$rS?kNyFc*Lpw@%HLhOgyr4yRe{UjP5Ow}qa%9-zLUrIah7-W!vdGT zTeObl-TkP*DGs3WF|S7_9~X6rf0gBpJt=V6|4UikvZn+t=hr`=SJK{W~nL z^|ZjHep*@H3dWa+%i0+%Z!_bpZtce`?`g&ZOrK(TtG_D%iJ!&tuFb;_v%EVPm%ncl zXL%poFWR}6+aJgB2EHeNJ&eD^@*ZVe{;o_P%e&)%AY9M%vsm5}j4$Nz`YjYfdaHgm zm;QE^cRS@4JjI<*j^C0LK}h$MT+L{3OrQzsU9IB|#|f`%RX2*UJKzzfZD?*h?BOO<&FKb0IC`Pt5@i`nemg1e~aZ^@`eDUA6~=q9%g(#_s1pe z`AtDDfA8WsmiG~!C?9b98(H25e=UFt#wWA9`x!sQ_|JLW+4~y-yvz8FEbp%03INSI zrIO{{%Q)PZQ=Vse_c1Q-+s*PGVSFvqPholeJg`R@{{`eG`#ime$I!%5dOh*0qo#*-e&*2mfW_;4&Wj6Z_}&wE~y&-otv>%Xl-lb0O=&suT1S zPgC)*yy<`dr2Rj_@-DO)XZp1)@7Rz4#)@V%FUwoac$o16Ebn^8<#=_lyj_epGkp=u z`wHWy_`Z*^ya($AAoY1Q%bSh}{1~_6V|kZF1zyGN9OCt;hc}=Brte{S_vGPzmiH** z(r%AJ5VDi84WeN`uR9r*H#|q+a=fOoyz6;`Qp5NC5wAx(^6*}kcR%A&&(m4nBaBOZ z{*wLkF~+6c?qGSF=ZXGEy~SAGit}>ulkA_5GA{SCyII~Xiv@iL`axw0%X{LI+;;va z`{xIj<>Fspc~@U5@HWwm*2wZ6V7!O%f3<|3tCtIU`JV0jS>C;?1kS^wtz>zRuNJtB zlj#d8G+w)|5V)V)-^cRqWBftJn_1pvR|9AC!eyqm=GrsJX=`5xq-@p^RdI)U%v`+kPyUDhFR zX`eG$UaM2!N11+**P}NWm+vXw#q#!a3;ONigzxnUxjsk;K)z4-6t72XZxFa#U-q!P z@6sO^qR99BE?{{NY!dikank~jK_cD`21dz--bvwplRZ`BTg&t?1o%iAn*aarqNc^~||0HpqlSl-oN z5V*AG$5`GsZWp+`@6{~t4&IR6%lGxMyp?wddOzdOqUzXZ9^S+9u4nuWruUB(^np7? zJ5ZHVj`F$_xJv+oOrK$S%XSG|_A|ip9%j6s>0jq{XYSpCUh2P>KF0FiyI0W5_jsP3`4uxq}C4 zJ>z$>9u6}u^GkKChr#E#pW>t)W&eEWc>(N!KcbRhJv1K@cv`?3<)292HyD@wtl;tO z_@SU5%k)QBt}TpLGTzT}?PgrY3DrElhZ*-X{V|s71mgk52UxDLKN9zq_#__Rg^bJg ze-&9KvleLF}1wc^Zs|m^8T)I5Sxo4^)i-?evg5c|r-NOwzG&u<=K;h1RE5{R-u?e6jUKw zg`z5-3fU?YQTbHJR-t;8Plaq13afl7gg@8M*JnsUf+}RIP*CMlA-e#i^4SVxt57JQ zPz6=UR-s6aJ)xFoPYM!jP#{}{VhT}Eg=`gys(dPBt58JcQz2W0>Qz1!vQ;Ro@~M!m zLLrq;g=`fHs(dPB7l6Q*70=jeGb&`OP$-}>s*tTh^*OgkEzi6ZB-o%pwhF}*qM!=d zDil@uRLEALh{~rzwhGm&d@5wCP*~+tAzOt)DxV73Dil=tRLCv>fiK&<)Mji2vQ;P) zP^f|`WUEj(=k}=OnU{hD8x+V^p_oDxR3Tf1qAH&X*(wxK`BcbOp?Z~1g=`fHt9&YC zt58VgQz2W0f-0X1;oX_}<7X>KP=#z2!uOf;n+mFstwNz3dqOSGo)iS%L(V6%RVbzq z;XBO=p9)14B78qu;ZvcALWJ*HD|{+cuMpvT+X|lwg%u)v2VCJ(p^!p^?~^NhDil0rPCj|*MC=kBvp3fIk5PTs&p9o)^ z&xfK4g0J%D6X8qv`A|eb@DhN0B7C7gAF5XnygneG2rmc7hr$Yi7Z2nU;Y9=aP)I@W z%7T0%ytE)63Kl>V2rod$=d%?AuSrmdR45cssPKjbg|PsXV^4NIM}vX{RmfJMn98R@ zcn?JW{h|sIR3W@UBA+j!AVC$vyC?Gb>J=oYLU?OMK3`Ztf+~dfUF7qH6eOrZcvD6` zUrA3~Zv7B<0u4Wa9Gla*q4!`oKYEbZgk$B0hBV{oTb;eR|TR7%B-g1=o7GDV&?oZprz#DDWWg8#g{ Imh<`lH%BW|H~;_u diff --git a/crates/wasmtime/foo.o b/crates/wasmtime/foo.o deleted file mode 100644 index 5aa5f90d777545f33ac8c0b7fbeec3ef29514122..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9760 zcmeI2ziSjh6vyB0o;iy|7a~E$m?dZ<63!Sb6!b39fDjPG%4Toxvdb>qk8ryeqbURt zti{Sgu}Bf)AD|YZ{slIY+SC>b*KBjdH@h>pKkgJ<~*X=KwxhwjyI(ZiN)1n?#6+Wi(4y}Q7Fmiw#AP2|+a)2Bl2gm_(fE*wP$N_TT zKXBj#Kcn*KE)+J-D*E_LL@wgz8Js_U?MGvc>tJeDDOHZ?uhiGd^2BuIMInB`t_Q5( zG=9f=Q8%rxOiTl-8{i!0y1})AR9(~!>Z0_(aSAUhsHqzZ;+*$67qK|cHO`;%s-pjG zVkwL24jR?#wR8EmlFO?!uKS+E8)*1{F{W=Bzz3N*r@M&RighS;ddmwC*kKM z93EJHGw_0MbcyQ|)MmiQUspr&P89or8QnR9u51Vp9NXtz6AT@FP(eK#dy#2+Hv zj@?WOMVbs@=c?OYBhj!!WeLmjd^fE@5_Gz)dNwUaCNsu#@M+++Tjo6Ez$L(3tGU*0 zcrDw+D621J9+v>-0(&EfRgvAcYdV;3!<_H>K_{?kxD)HcW)!oMt5#s)Z}+c*{h!zz zx@n~I%3>9ba&eL85B*h*IAKfV@s;&Tj|B`E%ja(yX;re%=d<@+K9{Mt4fdJ5bB^G? ofb%GWI8Me3u=kV3_vz)sY#h0{hPe<W7yD1s-J}?_}h>vt7#loCUZ6(Ea*DfnK>Y>l9$l*-(^T2d;b@tbTg&QPD7p>o&3O zL1)hCz&zHadfkM&{(*64Q;~ap>|2!cYXw<-$NCp!SnH=%hu??Smt^&Al7813O&3NE zkOSlZIY17O1LOcXKn{=t@-mt#2_k$&pvnEE*7VWxk|(!bPuRpR3r ze5pZ>yx9=k&rzQT>mc6p`I6r Date: Thu, 26 Aug 2021 16:47:56 -0700 Subject: [PATCH 03/17] Improve AArch64 codegen --- cranelift/codegen/src/isa/aarch64/inst/emit.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 0e7f93efc708..d46a24402250 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -23,10 +23,10 @@ pub fn memlabel_finalize(_insn_off: CodeOffset, label: &MemLabel) -> i32 { /// is used when relative 26-bit call instructions won't cut it and a longer /// jump is needed. /// -/// This generats: +/// This generates: /// -/// adr x16, 16 -/// ldur x16, [x17] +/// ldr x16, 16 +/// adr x17, 12 /// add x16, x16, x17 /// br x16 /// @@ -36,10 +36,10 @@ pub fn memlabel_finalize(_insn_off: CodeOffset, label: &MemLabel) -> i32 { /// Note that this is part of the `MachBackend::gen_jump_veneer` contract. pub fn gen_jump_veneer() -> (u32, u32, u32, u32) { ( + // ldr x16, 16 + enc_ldst_imm19(0b01011000, 16 / 4, xreg(16)), // adr x17, 16 - enc_adr(16, writable_xreg(17)), - // ldr x16, [x17] - enc_ldst_simm9(0b1111100001, SImm9::zero(), 0b00, xreg(17), xreg(16)), + enc_adr(12, writable_xreg(17)), // add x16, x16, x17 enc_arith_rrr(0b10001011_000, 0, writable_xreg(16), xreg(16), xreg(17)), // br x16 From c2665bf79418c4949e4d891d0c8c437fa362b2a6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 26 Aug 2021 16:58:47 -0700 Subject: [PATCH 04/17] Add a compiler option to force jump veneers Only intended to be used during testing/debugging. This also hooks up the option to fuzzers. --- .../codegen/src/isa/aarch64/inst/emit.rs | 10 +++-- crates/cranelift/src/builder.rs | 35 ++++++++++++------ crates/cranelift/src/compiler.rs | 12 +++--- crates/cranelift/src/obj.rs | 7 +++- crates/fuzzing/src/generators.rs | 6 +++ tests/all/relocs.rs | 37 +++++++------------ 6 files changed, 62 insertions(+), 45 deletions(-) diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index d46a24402250..1b7eb0b645f2 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -25,10 +25,12 @@ pub fn memlabel_finalize(_insn_off: CodeOffset, label: &MemLabel) -> i32 { /// /// This generates: /// -/// ldr x16, 16 -/// adr x17, 12 -/// add x16, x16, x17 -/// br x16 +/// ```ignore +/// ldr x16, 16 +/// adr x17, 12 +/// add x16, x16, x17 +/// br x16 +/// ``` /// /// and the expectation is that the 8-byte immediate address to jump to is /// located after these instructions are encoded. diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs index 41c6560ac687..98630c819e9d 100644 --- a/crates/cranelift/src/builder.rs +++ b/crates/cranelift/src/builder.rs @@ -13,11 +13,20 @@ use wasmtime_environ::{CompilerBuilder, Setting, SettingKind}; struct Builder { flags: settings::Builder, isa_flags: isa::Builder, + linkopts: LinkOptions, +} - // A debug-only setting used to synthetically insert 0-byte padding between - // compiled functions to simulate huge compiled artifacts and exercise logic - // related to jump veneers. - padding_between_functions: usize, +#[derive(Clone, Default)] +pub struct LinkOptions { + /// A debug-only setting used to synthetically insert 0-byte padding between + /// compiled functions to simulate huge compiled artifacts and exercise + /// logic related to jump veneers. + pub padding_between_functions: usize, + + /// A debug-only setting used to force inter-function calls in a wasm module + /// to always go through "jump veneers" which are typically only generated + /// when functions are very far from each other. + pub force_jump_veneers: bool, } pub fn builder() -> Box { @@ -37,7 +46,7 @@ pub fn builder() -> Box { Box::new(Builder { flags, isa_flags: cranelift_native::builder().expect("host machine is not a supported target"), - padding_between_functions: 0, + linkopts: LinkOptions::default(), }) } @@ -56,12 +65,17 @@ impl CompilerBuilder for Builder { } fn set(&mut self, name: &str, value: &str) -> Result<()> { - // Special wasmtime-cranelift-only setting. - if name == "padding_between_functions" { - self.padding_between_functions = value.parse()?; + // Special wasmtime-cranelift-only settings first + if name == "linkopt_padding_between_functions" { + self.linkopts.padding_between_functions = value.parse()?; + return Ok(()); + } + if name == "linkopt_force_jump_veneer" { + self.linkopts.force_jump_veneers = value == "true"; return Ok(()); } + // ... then forward this to Cranelift if let Err(err) = self.flags.set(name, value) { match err { SetError::BadName(_) => { @@ -92,10 +106,7 @@ impl CompilerBuilder for Builder { .isa_flags .clone() .finish(settings::Flags::new(self.flags.clone())); - Box::new(crate::compiler::Compiler::new( - isa, - self.padding_between_functions, - )) + Box::new(crate::compiler::Compiler::new(isa, self.linkopts.clone())) } fn settings(&self) -> Vec { diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 1053edcc5ad5..d034655c6f62 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -1,3 +1,4 @@ +use crate::builder::LinkOptions; use crate::debug::ModuleMemoryOffset; use crate::func_environ::{get_func_name, FuncEnvironment}; use crate::obj::ObjectBuilder; @@ -36,15 +37,15 @@ use wasmtime_environ::{ pub(crate) struct Compiler { translators: Mutex>, isa: Box, - padding_between_functions: usize, + linkopts: LinkOptions, } impl Compiler { - pub(crate) fn new(isa: Box, padding_between_functions: usize) -> Compiler { + pub(crate) fn new(isa: Box, linkopts: LinkOptions) -> Compiler { Compiler { translators: Default::default(), isa, - padding_between_functions, + linkopts, } } @@ -256,6 +257,7 @@ impl wasmtime_environ::Compiler for Compiler { } let mut builder = ObjectBuilder::new(obj, &translation.module); + builder.force_jump_veneers = self.linkopts.force_jump_veneers; let mut addrs = AddressMapSection::default(); let mut traps = TrapEncodingBuilder::default(); @@ -263,8 +265,8 @@ impl wasmtime_environ::Compiler for Compiler { let range = builder.func(i, func); addrs.push(range.clone(), &func.address_map.instructions); traps.push(range, &func.traps); - if self.padding_between_functions > 0 { - builder.append_synthetic_padding(self.padding_between_functions); + if self.linkopts.padding_between_functions > 0 { + builder.append_synthetic_padding(self.linkopts.padding_between_functions); } } for (i, (body, func)) in trampolines.iter_mut() { diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index e5e0e6a956e3..f26b73677b1e 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -163,6 +163,10 @@ pub struct ObjectBuilder<'a> { /// call instruction. These relocations will get filled in at the end /// with relative offsets to their target. relative_relocs: Vec, + + /// A debug-only option to indicate that all inter-function calls should go + /// through veneers, ideally testing the veneer emission code. + pub force_jump_veneers: bool, } /// A pending relocation against a wasm-defined function that needs to be @@ -256,6 +260,7 @@ impl<'a> ObjectBuilder<'a> { current_text_off: 0, text_locations: HashMap::new(), relative_relocs: Vec::new(), + force_jump_veneers: false, } } @@ -480,7 +485,7 @@ impl<'a> ObjectBuilder<'a> { // added for a symbol but the symbol was defined very long // ago. The only way to reach the symbol at this point // is via a veneer. - if distance < min_neg { + if distance < min_neg || self.force_jump_veneers { self.emit_jump_veneer(r); // This case, if hit, represents a programming error. If diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index efe9302ce884..373659c0120a 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -20,6 +20,7 @@ use arbitrary::{Arbitrary, Unstructured}; pub struct DifferentialConfig { strategy: DifferentialStrategy, opt_level: OptLevel, + force_jump_veneers: bool, } impl DifferentialConfig { @@ -30,6 +31,11 @@ impl DifferentialConfig { DifferentialStrategy::Lightbeam => wasmtime::Strategy::Lightbeam, })?; config.cranelift_opt_level(self.opt_level.to_wasmtime()); + if self.force_jump_veneers { + unsafe { + config.cranelift_flag_set("linkopt_force_jump_veneer", "true")?; + } + } Ok(config) } } diff --git a/tests/all/relocs.rs b/tests/all/relocs.rs index 0bc64940ba9e..1d9f86ac4c0c 100644 --- a/tests/all/relocs.rs +++ b/tests/all/relocs.rs @@ -14,7 +14,7 @@ fn store_with_padding(padding: usize) -> Result> { // This is an internal debug-only setting specifically recognized for // basically just this set of tests. unsafe { - config.cranelift_flag_set("padding_between_functions", &padding.to_string())?; + config.cranelift_flag_set("linkopt_padding_between_functions", &padding.to_string())?; } let engine = Engine::new(&config)?; Ok(Store::new(&engine, ())) @@ -64,8 +64,20 @@ fn backwards_call_works() -> Result<()> { #[test] fn mixed() -> Result<()> { - let mut store = store_with_padding(MB)?; + test_many_call_module(store_with_padding(MB)?) +} + +#[test] +fn mixed_forced() -> Result<()> { + let mut config = Config::new(); + unsafe { + config.cranelift_flag_set("linkopt_force_jump_veneer", &padding.to_string())?; + } + let engine = Engine::new(&config)?; + test_many_call_module(Store::new(&engine, ())) +} +fn test_many_call_module(store: Store<()>) -> Result<()> { const N: i32 = 200; let mut wat = String::new(); @@ -96,24 +108,3 @@ fn mixed() -> Result<()> { } Ok(()) } - -#[test] -fn forward_huge() -> Result<()> { - let mut store = store_with_padding(2 * GB)?; - let module = Module::new( - store.engine(), - r#" - (module - (func (export "foo") (result i32) - call 1) - (func (result i32) - i32.const 4) - ) - "#, - )?; - - let i = Instance::new(&mut store, &module, &[])?; - let foo = i.get_typed_func::<(), i32, _>(&mut store, "foo")?; - assert_eq!(foo.call(&mut store, ())?, 4); - Ok(()) -} From f0bbc6b84300d091f6009813cbaaa2861b71d88f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 26 Aug 2021 19:12:36 -0700 Subject: [PATCH 05/17] More typos --- cranelift/codegen/src/isa/aarch64/inst/emit.rs | 2 +- cranelift/codegen/src/isa/x64/inst/emit.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 1b7eb0b645f2..e8541033561c 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -40,7 +40,7 @@ pub fn gen_jump_veneer() -> (u32, u32, u32, u32) { ( // ldr x16, 16 enc_ldst_imm19(0b01011000, 16 / 4, xreg(16)), - // adr x17, 16 + // adr x17, 12 enc_adr(12, writable_xreg(17)), // add x16, x16, x17 enc_arith_rrr(0b10001011_000, 0, writable_xreg(16), xreg(16), xreg(17)), diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index b9a9f8b35b74..6f26cf74c23e 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -62,10 +62,12 @@ fn emit_reloc( /// /// This generats: /// -/// movabsq $val, %r10 -/// lea -15(%rip), %r11 -/// add %r10, %r11 -/// jmpq *%r11 +/// ```ignore +/// movabsq $val, %r10 +/// lea -15(%rip), %r11 +/// add %r10, %r11 +/// jmpq *%r11 +/// ``` /// /// Note that this is part of the `MachBackend::gen_jump_veneer` contract. pub fn gen_jump_veneer() -> (Vec, usize) { From d4751943d9d6f3b7a5d5479894f32dc409ec918d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 09:16:49 -0700 Subject: [PATCH 06/17] Rebase issues and comments --- cranelift/codegen/src/isa/x64/inst/emit.rs | 2 +- crates/cranelift/src/builder.rs | 6 +++--- crates/cranelift/src/compiler.rs | 17 ++++++++--------- crates/cranelift/src/obj.rs | 2 +- crates/fuzzing/src/generators.rs | 2 +- tests/all/relocs.rs | 7 +++++-- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 6f26cf74c23e..984ede809279 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -60,7 +60,7 @@ fn emit_reloc( /// used when relative 32-bit call instructions won't cut it and a longer jump /// is needed. /// -/// This generats: +/// This generates: /// /// ```ignore /// movabsq $val, %r10 diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs index 98630c819e9d..905b3814bd41 100644 --- a/crates/cranelift/src/builder.rs +++ b/crates/cranelift/src/builder.rs @@ -66,12 +66,12 @@ impl CompilerBuilder for Builder { fn set(&mut self, name: &str, value: &str) -> Result<()> { // Special wasmtime-cranelift-only settings first - if name == "linkopt_padding_between_functions" { + if name == "wasmtime_linkopt_padding_between_functions" { self.linkopts.padding_between_functions = value.parse()?; return Ok(()); } - if name == "linkopt_force_jump_veneer" { - self.linkopts.force_jump_veneers = value == "true"; + if name == "wasmtime_linkopt_force_jump_veneer" { + self.linkopts.force_jump_veneers = value.parse()?; return Ok(()); } diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index d034655c6f62..231e82ab01a5 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -204,10 +204,9 @@ impl wasmtime_environ::Compiler for Compiler { None }; - Ok(( + Ok(Box::new(( code_buf, - Box::new(CompiledFunction { - body: code_buf, + CompiledFunction { jt_offsets: context.func.jt_offsets, relocations: reloc_sink.func_relocs, value_labels_ranges: ranges.unwrap_or(Default::default()), @@ -219,8 +218,8 @@ impl wasmtime_environ::Compiler for Compiler { stack_maps: stack_map_sink.finish(), }, address_map: address_transform, - }), - )) + }, + ))) } fn emit_obj( @@ -256,13 +255,13 @@ impl wasmtime_environ::Compiler for Compiler { trampolines.push((i, func)); } - let mut builder = ObjectBuilder::new(obj, &translation.module); + let mut builder = ObjectBuilder::new(obj, &translation.module, &*self.isa); builder.force_jump_veneers = self.linkopts.force_jump_veneers; let mut addrs = AddressMapSection::default(); let mut traps = TrapEncodingBuilder::default(); - for (i, func) in funcs.iter() { - let range = builder.func(i, func); + for ((i, func), (_, body)) in funcs.iter().zip(bodies) { + let range = builder.func(i, func, body); addrs.push(range.clone(), &func.address_map.instructions); traps.push(range, &func.traps); if self.linkopts.padding_between_functions > 0 { @@ -304,9 +303,9 @@ impl wasmtime_environ::Compiler for Compiler { builder.dwarf_sections(&dwarf_sections)?; } + builder.finish()?; addrs.append_to(obj); traps.append_to(obj); - builder.finish()?; Ok(funcs.into_iter().map(|(_, f)| f.info).collect()) } diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index f26b73677b1e..7ada8a8ba8e0 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -113,7 +113,7 @@ pub struct ObjectBuilder<'a> { /// a relocation against a libcall. libcalls: HashMap, - /// Packed form of windows unwind tables which, if present, will get emmited + /// Packed form of windows unwind tables which, if present, will get emitted /// to a windows-specific unwind info section. windows_unwind_info: Vec, diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index 373659c0120a..361298ca3cfa 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -33,7 +33,7 @@ impl DifferentialConfig { config.cranelift_opt_level(self.opt_level.to_wasmtime()); if self.force_jump_veneers { unsafe { - config.cranelift_flag_set("linkopt_force_jump_veneer", "true")?; + config.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true")?; } } Ok(config) diff --git a/tests/all/relocs.rs b/tests/all/relocs.rs index 1d9f86ac4c0c..643b118beab3 100644 --- a/tests/all/relocs.rs +++ b/tests/all/relocs.rs @@ -14,7 +14,10 @@ fn store_with_padding(padding: usize) -> Result> { // This is an internal debug-only setting specifically recognized for // basically just this set of tests. unsafe { - config.cranelift_flag_set("linkopt_padding_between_functions", &padding.to_string())?; + config.cranelift_flag_set( + "wasmtime_linkopt_padding_between_functions", + &padding.to_string(), + )?; } let engine = Engine::new(&config)?; Ok(Store::new(&engine, ())) @@ -71,7 +74,7 @@ fn mixed() -> Result<()> { fn mixed_forced() -> Result<()> { let mut config = Config::new(); unsafe { - config.cranelift_flag_set("linkopt_force_jump_veneer", &padding.to_string())?; + config.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", &padding.to_string())?; } let engine = Engine::new(&config)?; test_many_call_module(Store::new(&engine, ())) From 63c0e77b8b0af439b7c7a3f8e6132fb9675597af Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 09:19:25 -0700 Subject: [PATCH 07/17] Fix some CI issues --- cranelift/codegen/src/isa/arm32/mod.rs | 8 ++++++++ tests/all/relocs.rs | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cranelift/codegen/src/isa/arm32/mod.rs b/cranelift/codegen/src/isa/arm32/mod.rs index 477dc6ec4696..a4f76476957f 100644 --- a/cranelift/codegen/src/isa/arm32/mod.rs +++ b/cranelift/codegen/src/isa/arm32/mod.rs @@ -115,6 +115,14 @@ impl MachBackend for Arm32Backend { // Carry flag clear. IntCC::UnsignedLessThan } + + fn generate_jump_veneer(&self) -> (Vec, usize) { + panic!("not implemented for arm32 backend") + } + + fn max_jump_veneer_size(&self) -> usize { + 0 + } } /// Create a new `isa::Builder`. diff --git a/tests/all/relocs.rs b/tests/all/relocs.rs index 643b118beab3..5b54e66205da 100644 --- a/tests/all/relocs.rs +++ b/tests/all/relocs.rs @@ -3,6 +3,8 @@ //! for when platform-specific relative call instructios can't always reach //! their destination within the platform-specific limits. +#![cfg(not(feature = "old-x86-backend"))] // multi-value not supported here + use anyhow::Result; use wasmtime::*; @@ -74,13 +76,13 @@ fn mixed() -> Result<()> { fn mixed_forced() -> Result<()> { let mut config = Config::new(); unsafe { - config.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", &padding.to_string())?; + config.cranelift_flag_set("wasmtime_linkopt_force_jump_veneer", "true")?; } let engine = Engine::new(&config)?; test_many_call_module(Store::new(&engine, ())) } -fn test_many_call_module(store: Store<()>) -> Result<()> { +fn test_many_call_module(mut store: Store<()>) -> Result<()> { const N: i32 = 200; let mut wat = String::new(); From c1ab8052965b240c286b99f3b3e1ce326ca73349 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 10:21:26 -0700 Subject: [PATCH 08/17] Skip X86PCRelRodata4 ... but why? --- crates/cranelift/src/obj.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index 7ada8a8ba8e0..5f374936f6e8 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -363,6 +363,13 @@ impl<'a> ObjectBuilder<'a> { let (kind, encoding, size) = match r.reloc { Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), + + // TODO: why is this skipped? In a previous iteration of this + // file this was skipped without comment, and a recent + // refactoring led to this comment and confusion as to why this + // relocation is ignored... + Reloc::X86PCRelRodata4 => continue, + other => unimplemented!("Unimplemented relocation {:?}", other), }; self.obj From 41e191a88d1e4c4d7376155cfc60d65e2912a106 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 11:58:16 -0700 Subject: [PATCH 09/17] Fix windows Don't push raw data onto the text section, we have to account for this separately. Add a debug assertion to catch bugs like this in the future. --- crates/cranelift/src/obj.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index 5f374936f6e8..c392a0edae2a 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -301,9 +301,7 @@ impl<'a> ObjectBuilder<'a> { let unwind_size = info.emit_size(); let mut unwind_info = vec![0; unwind_size]; info.emit(&mut unwind_info); - let unwind_off = self - .obj - .append_section_data(self.text_section, &unwind_info, 4); + let unwind_off = self.push_code(unwind_info, true); self.windows_unwind_info.push(RUNTIME_FUNCTION { begin: u32::try_from(off).unwrap(), end: u32::try_from(off + body_len).unwrap(), @@ -725,8 +723,9 @@ impl<'a> ObjectBuilder<'a> { // With relocations all handled we can shove everything into the final // text section now. - for contents in self.text_contents.iter() { - self.obj.append_section_data(self.text_section, contents, 1); + for (i, contents) in self.text_contents.iter().enumerate() { + let off = self.obj.append_section_data(self.text_section, contents, 1); + debug_assert_eq!(self.text_locations[&off], i); } if self.windows_unwind_info.len() > 0 { From 0c47bde118df58ef5118a3d58ca28e5a2dd7197f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 12:01:39 -0700 Subject: [PATCH 10/17] Clarify a comment --- crates/cranelift/src/obj.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index c392a0edae2a..97432dd8688a 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -362,10 +362,11 @@ impl<'a> ObjectBuilder<'a> { Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), - // TODO: why is this skipped? In a previous iteration of this - // file this was skipped without comment, and a recent - // refactoring led to this comment and confusion as to why this - // relocation is ignored... + // This is emitted by the old x86 backend and is only present + // for when the constant rodata is separated from the code + // itself. We don't do that, though, so we ignore these + // relocations since the offsets already listed here are already + // correct. Reloc::X86PCRelRodata4 => continue, other => unimplemented!("Unimplemented relocation {:?}", other), From 97853e4dedb5f1d511dc53c7157d5b6fa15a7d5a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 12:11:58 -0700 Subject: [PATCH 11/17] Add handling of alignment Hopefully can remove all this with gimli-rs/object#367 --- crates/cranelift/src/obj.rs | 40 ++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index 97432dd8688a..ac9f3f89279d 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -144,7 +144,10 @@ pub struct ObjectBuilder<'a> { /// end when after all relocations have been applied. Note that this is /// separate from the section in `obj` because these contents will be edited /// for relocations as necessary. - text_contents: Vec>, + /// + /// The second element of the pair here is the desired alignment of the + /// function body. + text_contents: Vec<(Vec, u64)>, /// Map from text section offset, `u64`, to the index in `text_contents` /// where those contents live. @@ -275,7 +278,7 @@ impl<'a> ObjectBuilder<'a> { body: Vec, ) -> (SymbolId, Range) { let body_len = body.len() as u64; - let off = self.push_code(body, true); + let off = self.push_code(body, 1, true); let symbol_id = self.obj.add_symbol(Symbol { name, @@ -301,7 +304,7 @@ impl<'a> ObjectBuilder<'a> { let unwind_size = info.emit_size(); let mut unwind_info = vec![0; unwind_size]; info.emit(&mut unwind_info); - let unwind_off = self.push_code(unwind_info, true); + let unwind_off = self.push_code(unwind_info, 4, true); self.windows_unwind_info.push(RUNTIME_FUNCTION { begin: u32::try_from(off).unwrap(), end: u32::try_from(off + body_len).unwrap(), @@ -396,23 +399,24 @@ impl<'a> ObjectBuilder<'a> { /// function's relative call may reach further than the function being /// inserted, then a "veneer" is inserted to help it jump further, but the /// veneer needs to be inserted before `body` is inserted. - fn push_code(&mut self, body: Vec, allow_veneers: bool) -> u64 { + fn push_code(&mut self, body: Vec, align: u64, allow_veneers: bool) -> u64 { let body_len = body.len() as u64; assert!(body_len > 0); // If this function would exceed the `reloc_deadline`, then all // relocations are processed to force some veneers to get generated. - while self.current_text_off + body_len >= self.reloc_deadline { + let mut ret = align_to(self.current_text_off, align); + while ret + body_len >= self.reloc_deadline { assert!(allow_veneers); - self.emit_jump_veneers(self.current_text_off + body_len); + self.emit_jump_veneers(ret + body_len); + ret = align_to(self.current_text_off, align); } // Once we can safely append the body we do so, updating various state // about how to get back to the body. - let ret = self.current_text_off; self.text_locations.insert(ret, self.text_contents.len()); self.current_text_off += body_len; - self.text_contents.push(body); + self.text_contents.push((body, align)); return ret; } @@ -558,7 +562,7 @@ impl<'a> ObjectBuilder<'a> { let endian = self.endian(); let code = self.text_locations[&r.offset]; - let code = &mut self.text_contents[code]; + let code = &mut self.text_contents[code].0; match r.reloc.reloc { // This corresponds to the `R_AARCH64_CALL26` ELF relocation. @@ -595,7 +599,7 @@ impl<'a> ObjectBuilder<'a> { /// patched to jump to the veneer we're synthesizing. fn emit_jump_veneer(&mut self, r: PendingReloc<'a>) { let (veneer, reloc_offset) = generate_jump_veneer(self.isa); - let veneer_offset = self.push_code(veneer, false); + let veneer_offset = self.push_code(veneer, 1, false); self.relative_relocs.push(RelativeReloc { veneer_offset, reloc_offset, @@ -624,7 +628,7 @@ impl<'a> ObjectBuilder<'a> { /// Helper function exclusively for tests to increase padding between /// functions to test the veneer insertion logic in this file. pub fn append_synthetic_padding(&mut self, amt: usize) { - self.push_code(vec![0; amt], true); + self.push_code(vec![0; amt], 1, true); } /// Inserts a compiled trampoline into this object. @@ -716,7 +720,7 @@ impl<'a> ObjectBuilder<'a> { .wrapping_sub(reloc_offset); // Store the `value` into the location specified in the relocation. - let code = &mut self.text_contents[self.text_locations[&reloc.veneer_offset]]; + let code = &mut self.text_contents[self.text_locations[&reloc.veneer_offset]].0; assert_eq!(self.isa.pointer_type().bits(), 64); reloc_address::>(code, reloc.reloc_offset as u32) .set(endian, value); @@ -724,8 +728,10 @@ impl<'a> ObjectBuilder<'a> { // With relocations all handled we can shove everything into the final // text section now. - for (i, contents) in self.text_contents.iter().enumerate() { - let off = self.obj.append_section_data(self.text_section, contents, 1); + for (i, (contents, align)) in self.text_contents.iter().enumerate() { + let off = self + .obj + .append_section_data(self.text_section, contents, *align); debug_assert_eq!(self.text_locations[&off], i); } @@ -899,6 +905,12 @@ impl<'a> ObjectBuilder<'a> { } } +/// Align a size up to a power-of-two alignment. +fn align_to(x: u64, alignment: u64) -> u64 { + let alignment_mask = alignment - 1; + (x + alignment_mask) & !alignment_mask +} + impl PendingReloc<'_> { /// Returns the maximum negative offset and maximum positive offset that /// this relocation can reach. From 1323014d1897cfa7638eb0d6d997f5115b5808c7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 13:27:57 -0700 Subject: [PATCH 12/17] More alignment fixes --- crates/cranelift/src/obj.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index ac9f3f89279d..85b2f95b4410 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -415,7 +415,7 @@ impl<'a> ObjectBuilder<'a> { // Once we can safely append the body we do so, updating various state // about how to get back to the body. self.text_locations.insert(ret, self.text_contents.len()); - self.current_text_off += body_len; + self.current_text_off = ret + body_len; self.text_contents.push((body, align)); return ret; } From 30391404d44b79d555cd189a97317dd7e7110a57 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 14:21:04 -0700 Subject: [PATCH 13/17] Fix warning --- tests/all/relocs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/all/relocs.rs b/tests/all/relocs.rs index 5b54e66205da..d767eddbca00 100644 --- a/tests/all/relocs.rs +++ b/tests/all/relocs.rs @@ -9,7 +9,6 @@ use anyhow::Result; use wasmtime::*; const MB: usize = 1 << 20; -const GB: usize = 1 << 30; fn store_with_padding(padding: usize) -> Result> { let mut config = Config::new(); From 6f64ab435eba9c07989a38c3913509892c96f8ea Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 14:41:01 -0700 Subject: [PATCH 14/17] Fix some encoding --- cranelift/codegen/src/isa/x64/inst/emit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 984ede809279..bb943815112c 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -85,13 +85,13 @@ pub fn gen_jump_veneer() -> (Vec, usize) { // lea -15(%rip), %r11 bytes.push(0x48 | ((r11 >> 3) & 1) << 2); bytes.push(0x8d); - bytes.push(encode_modrm(0b00, r11, 0b101)); + bytes.push(encode_modrm(0b00, r11 & 7, 0b101)); bytes.extend_from_slice(&i32::to_le_bytes(-15)); // add %r10, %r11 bytes.push(0x48 | (((r11 >> 3) & 1) << 2) | ((r10 >> 3) & 1)); bytes.push(0x01); - bytes.push(encode_modrm(0b11, r10, r11)); + bytes.push(encode_modrm(0b11, r10 & 7, r11 & 7)); // jmpq *%r11 bytes.push(0x40 | ((r11 >> 3) & 1)); From cb119a66b8a1018b114ae8496a0ca01548cca9c3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 16:12:44 -0700 Subject: [PATCH 15/17] Disable fail-fast to see failures --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8a2e2b5d384e..6227f988c9d0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -225,6 +225,7 @@ jobs: env: QEMU_BUILD_VERSION: 6.0.0 strategy: + fail-fast: false matrix: include: - os: ubuntu-latest From a50ccfd9715ca4bf9ebecd4b4a38beeb60ff530b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 17:30:05 -0700 Subject: [PATCH 16/17] Revert "Disable fail-fast to see failures" This reverts commit cb119a66b8a1018b114ae8496a0ca01548cca9c3. --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6227f988c9d0..8a2e2b5d384e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -225,7 +225,6 @@ jobs: env: QEMU_BUILD_VERSION: 6.0.0 strategy: - fail-fast: false matrix: include: - os: ubuntu-latest From faff1d449f034e5d5ba7327ade3bd948936480e4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Aug 2021 17:58:57 -0700 Subject: [PATCH 17/17] Don't double-add the addend --- crates/cranelift/src/obj.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index 85b2f95b4410..bddc9954ae51 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -202,8 +202,6 @@ struct RelativeReloc { /// from the location of the relocation to this function's definition will /// be written. target: DefinedFuncIndex, - /// An optional addend for the relocation to add. - addend: i64, } // This is a mirror of `RUNTIME_FUNCTION` in the Windows API, but defined here @@ -604,7 +602,6 @@ impl<'a> ObjectBuilder<'a> { veneer_offset, reloc_offset, target: r.target, - addend: i64::from(r.reloc.addend), }); self.patch_reloc(&r, r.relative_distance_to(veneer_offset)); } @@ -715,9 +712,7 @@ impl<'a> ObjectBuilder<'a> { let symbol = self.func_symbols[target]; let target_offset = self.obj.symbol(symbol).value; let reloc_offset = reloc.veneer_offset + (reloc.reloc_offset as u64); - let value = target_offset - .wrapping_add(reloc.addend as u64) - .wrapping_sub(reloc_offset); + let value = target_offset.wrapping_sub(reloc_offset); // Store the `value` into the location specified in the relocation. let code = &mut self.text_contents[self.text_locations[&reloc.veneer_offset]].0;