aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorGuillaume Melquiond2020-12-27 11:24:07 +0100
committerGuillaume Melquiond2021-01-10 10:24:10 +0100
commitee5fa81585a67cc556b19e145f518b641c40ffb7 (patch)
tree39878a20f2aa80ea5626a7fcd63de97b0929e57f /kernel
parent944757e10651fda0d63d9291a6bcb1b6fdbaa256 (diff)
Add a peephole optimization for PUSHFIELDS(1).
The PUSHFIELDS opcode is a costly one, yet lots of constructors have at most one usable argument (e.g., option, nat, positive, Z, Acc). For those constructors, PUSHFIELDS(1) is replaced by GETFIELD(0);PUSH, assuming the accu register is no longer used afterwards. Replacing one single opcode by two opcodes might seem like a pessimization, but it is not. Indeed, pattern-matching branches usually start by filling the accu register with a constructor argument or the value of a free variable or a constant. All of those offer peephole optimizations for PUSH, which means that the number of opcodes actually stay constant. Note that, for the same reason, the assumption above holds in practice: the accu register is no longer used after PUSHFIELDS.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/vmemitcodes.ml13
1 files changed, 13 insertions, 0 deletions
diff --git a/kernel/vmemitcodes.ml b/kernel/vmemitcodes.ml
index e70285d9ab..5849641c20 100644
--- a/kernel/vmemitcodes.ml
+++ b/kernel/vmemitcodes.ml
@@ -135,6 +135,16 @@ let out env opcode =
let is_immed i = Uint63.le (Uint63.of_int i) Uint63.maxuint31
+(* Detect whether the current value of the accu register is no longer
+ needed (i.e., the register is written before being read). If so, the
+ register can be used freely; no need to save and restore it. *)
+let is_accu_dead = function
+ | [] -> false
+ | c :: _ ->
+ match c with
+ | Kacc _ | Kenvacc _ | Kconst _ | Koffsetclosure _ | Kgetglobal _ -> true
+ | _ -> false
+
let out_int env n =
out_word env n (n asr 8) (n asr 16) (n asr 24)
@@ -399,6 +409,9 @@ let rec emit env insns remaining = match insns with
| Kpush :: Kconst const :: c ->
out env opPUSHGETGLOBAL; slot_for_const env const;
emit env c remaining
+ | Kpushfields 1 :: c when is_accu_dead c ->
+ out env opGETFIELD0;
+ emit env (Kpush :: c) remaining
| Kpop n :: Kjump :: c ->
out env opRETURN; out_int env n; emit env c remaining
| Ksequence c1 :: c ->