Cranelift: Deduplicate ABI signatures during lowering (#4829)
* Cranelift: Deduplicate ABI signatures during lowering This commit creates the `SigSet` type which interns and deduplicates the ABI signatures that we create from `ir::Signature`s. The ABI signatures are now referred to indirectly via a `Sig` (which is a `cranelift_entity` ID), and we pass around a `SigSet` to anything that needs to access the actual underlying `SigData` (which is what `ABISig` used to be). I had to change a couple methods to return a `SmallInstVec` instead of emitting directly to work around what would otherwise be shared and exclusive borrows of the lowering context overlapping. I don't expect any of these to heap allocate in practice. This does not remove the often-unnecessary allocations caused by `ensure_struct_return_ptr_is_returned`. That is left for follow up work. This also opens the door for further shuffling of signature data into more efficient representations in the future, now that we have `SigSet` to store it all in one place and it is threaded through all the code. We could potentially move each signature's parameter and return vectors into one big vector shared between all signatures, for example, which could cut down on allocations and shrink the size of `SigData` since those `SmallVec`s have pretty large inline capacity. Overall, this refactoring gives a 1-7% speedup for compilation on `pulldown-cmark`: ``` compilation :: cycles :: benchmarks/pulldown-cmark/benchmark.wasm Δ = 8754213.66 ± 7526266.23 (confidence = 99%) dedupe.so is 1.01x to 1.07x faster than main.so! [191003295 234620642.20 280597986] dedupe.so [197626699 243374855.86 321816763] main.so compilation :: cycles :: benchmarks/bz2/benchmark.wasm No difference in performance. [170406200 194299792.68 253001201] dedupe.so [172071888 193230743.11 223608329] main.so compilation :: cycles :: benchmarks/spidermonkey/benchmark.wasm No difference in performance. [3870997347 4437735062.59 5216007266] dedupe.so [4019924063 4424595349.24 4965088931] main.so ``` * Use full path instead of import to avoid warnings in some build configurations Warnings will then cause CI to fail. * Move `SigSet` into `VCode`
This commit is contained in:
@@ -4060,7 +4060,7 @@
|
||||
|
||||
;; Direct call to an in-range function.
|
||||
(rule (lower (call (func_ref_data sig_ref name (reloc_distance_near)) args))
|
||||
(let ((abi ABISig (abi_sig sig_ref))
|
||||
(let ((abi Sig (abi_sig sig_ref))
|
||||
(_ Unit (abi_accumulate_outgoing_args_size abi))
|
||||
(_ InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
|
||||
(_ InstOutput (side_effect (abi_call abi name (Opcode.Call)))))
|
||||
@@ -4068,7 +4068,7 @@
|
||||
|
||||
;; Direct call to an out-of-range function (implicitly via pointer).
|
||||
(rule (lower (call (func_ref_data sig_ref name _) args))
|
||||
(let ((abi ABISig (abi_sig sig_ref))
|
||||
(let ((abi Sig (abi_sig sig_ref))
|
||||
(_ Unit (abi_accumulate_outgoing_args_size abi))
|
||||
(_ InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
|
||||
(target Reg (load_symbol_reloc (SymbolReloc.Absolute name 0)))
|
||||
@@ -4077,7 +4077,7 @@
|
||||
|
||||
;; Indirect call.
|
||||
(rule (lower (call_indirect sig_ref ptr args))
|
||||
(let ((abi ABISig (abi_sig sig_ref))
|
||||
(let ((abi Sig (abi_sig sig_ref))
|
||||
(target Reg (put_in_reg ptr))
|
||||
(_ Unit (abi_accumulate_outgoing_args_size abi))
|
||||
(_ InstOutput (lower_call_args abi (range 0 (abi_num_args abi)) args))
|
||||
@@ -4085,14 +4085,14 @@
|
||||
(lower_call_rets abi (range 0 (abi_num_rets abi)) (output_builder_new))))
|
||||
|
||||
;; Lower function arguments.
|
||||
(decl lower_call_args (ABISig Range ValueSlice) InstOutput)
|
||||
(decl lower_call_args (Sig Range ValueSlice) InstOutput)
|
||||
(rule (lower_call_args abi range args)
|
||||
(let ((_ InstOutput (lower_call_args_buffer abi range args))
|
||||
(_ InstOutput (lower_call_args_slots abi range args)))
|
||||
(lower_call_ret_arg abi)))
|
||||
|
||||
;; Lower function arguments (part 1): prepare buffer copies.
|
||||
(decl lower_call_args_buffer (ABISig Range ValueSlice) InstOutput)
|
||||
(decl lower_call_args_buffer (Sig Range ValueSlice) InstOutput)
|
||||
(rule (lower_call_args_buffer abi (range_empty) _) (output_none))
|
||||
(rule (lower_call_args_buffer abi (range_unwrap head tail) args)
|
||||
(let ((_ InstOutput (copy_to_buffer 0 (abi_get_arg abi head)
|
||||
@@ -4100,7 +4100,7 @@
|
||||
(lower_call_args_buffer abi tail args)))
|
||||
|
||||
;; Lower function arguments (part 2): set up registers / stack slots.
|
||||
(decl lower_call_args_slots (ABISig Range ValueSlice) InstOutput)
|
||||
(decl lower_call_args_slots (Sig Range ValueSlice) InstOutput)
|
||||
(rule (lower_call_args_slots abi (range_empty) _) (output_none))
|
||||
(rule (lower_call_args_slots abi (range_unwrap head tail) args)
|
||||
(let ((_ Unit (copy_to_arg (abi_lane_order abi)
|
||||
@@ -4109,7 +4109,7 @@
|
||||
(lower_call_args_slots abi tail args)))
|
||||
|
||||
;; Lower function arguments (part 3): implicit return-area pointer.
|
||||
(decl lower_call_ret_arg (ABISig) InstOutput)
|
||||
(decl lower_call_ret_arg (Sig) InstOutput)
|
||||
(rule (lower_call_ret_arg (abi_no_ret_arg)) (output_none))
|
||||
(rule (lower_call_ret_arg abi @ (abi_ret_arg (abi_arg_only_slot slot)))
|
||||
(let ((ret_arg Reg (load_addr (memarg_stack_off (abi_sized_stack_arg_space abi) 0)))
|
||||
@@ -4117,7 +4117,7 @@
|
||||
(output_none)))
|
||||
|
||||
;; Lower function return values by collecting them from registers / stack slots.
|
||||
(decl lower_call_rets (ABISig Range InstOutputBuilder) InstOutput)
|
||||
(decl lower_call_rets (Sig Range InstOutputBuilder) InstOutput)
|
||||
(rule (lower_call_rets abi (range_empty) builder) (output_builder_finish builder))
|
||||
(rule (lower_call_rets abi (range_unwrap head tail) builder)
|
||||
(let ((ret ValueRegs (copy_from_arg (abi_lane_order abi)
|
||||
|
||||
Reference in New Issue
Block a user