From f67e999464a4096974cf754b50c0ff3fbee165ed Mon Sep 17 00:00:00 2001 From: Antal Spector-Zabusky Date: Wed, 15 Mar 2023 15:48:59 -0400 Subject: [PATCH 1/8] Local immutable arrays (ocaml-jst#156) * Move `caml_{i,}array_of_{i,}array` primitives to `array.c` * Make immutable array externals local-aware * Make immutable array literals support local contents * Add `%empty_iarray` * Bootstrap * Implement most of the local iarray API * Give `Iarray.init` a local callback * Comments and small responses to Richard * Revert "Add `%empty_iarray`" This reverts commit 70f6c0d1a4060b38d9edc24ac7ba6a0b2fc54dfd. * Replace references to `empty_iarray` with `unsafe_of_array [||]` * Text updates * Bootstrap * Comment clarification * Remove superfluous `unsafe_sub_of_array_local` * Add some missing iarray functions by avoiding `let mutable` * Drop some CRs * Resynchronize `iarray.mli` and `iarrayLabels.mli` * Make `iarray.ml` and `iarrayLabels.mli` being copies more prominent * Fix comment * [minor] Rename a local C variable * Update `%array_{un,}safe_set` to be local-aware * Add test demonstrating broken local `float iarray` access allocation * Add clarifying comments on [Parrayset{u,s}] * Missing mode selection for `Parraysetu` * Allow accessing local flat float (i)arrays to locally allocate This is currently broken for pattern matching but works for everything else, I think! * Bootstrap * Confirm that local flat float array acccess doesn't allocate * Add test for local flat float array pattern matching allocating * Move the allocation-on-flat-float-array-matching to a known TODO * Update `Parrayref{u,s}` and `Parrayset{u,s}` to take custom types * Propagate the `array_{ref,set}_kind` types further * Bootstrap * Respond to review: small refactoring in `translprim.ml` * Respond to review: eta-expansion * Respond to review: Add some more warning comments --- ocaml/asmcomp/cmm_helpers.ml | 134 +++-- ocaml/asmcomp/cmm_helpers.mli | 18 +- ocaml/asmcomp/cmmgen.ml | 24 +- ocaml/boot/ocamlc | Bin 3480681 -> 3483429 bytes ocaml/boot/ocamllex | Bin 372168 -> 372351 bytes ocaml/bytecomp/bytegen.ml | 29 +- ocaml/lambda/lambda.ml | 52 +- ocaml/lambda/lambda.mli | 30 +- ocaml/lambda/matching.ml | 6 +- ocaml/lambda/printlambda.ml | 30 +- ocaml/lambda/transl_array_comprehension.ml | 7 +- ocaml/lambda/translprim.ml | 140 +++-- ocaml/middle_end/clambda_primitives.ml | 22 +- ocaml/middle_end/clambda_primitives.mli | 22 +- ocaml/middle_end/closure/closure.ml | 8 +- ocaml/middle_end/convert_primitives.ml | 8 +- .../middle_end/flambda/inline_and_simplify.ml | 20 +- ocaml/middle_end/flambda/inlining_cost.ml | 8 +- ocaml/middle_end/printclambda_primitives.ml | 32 +- ocaml/middle_end/semantics_of_primitives.ml | 4 +- ocaml/runtime/array.c | 127 +++- ocaml/runtime/str.c | 10 - ocaml/stdlib/iarray.ml | 569 ++++++++++++++++-- ocaml/stdlib/iarray.mli | 329 +++++++++- ocaml/stdlib/iarrayLabels.ml | 566 +++++++++++++++-- ocaml/stdlib/iarrayLabels.mli | 332 +++++++++- .../testsuite/tests/lib-array/test_iarray.ml | 2 +- ocaml/testsuite/tests/typing-local/alloc.ml | 4 +- .../typing-local/float_iarray.heap.reference | 4 + .../tests/typing-local/float_iarray.ml | 74 +++ .../typing-local/float_iarray.stack.reference | 4 + .../tests/typing-local/iarray.heap.reference | 14 + ocaml/testsuite/tests/typing-local/iarray.ml | 144 +++++ .../tests/typing-local/iarray.stack.reference | 14 + ocaml/typing/typecore.ml | 10 +- 35 files changed, 2451 insertions(+), 346 deletions(-) create mode 100644 ocaml/testsuite/tests/typing-local/float_iarray.heap.reference create mode 100644 ocaml/testsuite/tests/typing-local/float_iarray.ml create mode 100644 ocaml/testsuite/tests/typing-local/float_iarray.stack.reference create mode 100644 ocaml/testsuite/tests/typing-local/iarray.heap.reference create mode 100644 ocaml/testsuite/tests/typing-local/iarray.ml create mode 100644 ocaml/testsuite/tests/typing-local/iarray.stack.reference diff --git a/ocaml/asmcomp/cmm_helpers.ml b/ocaml/asmcomp/cmm_helpers.ml index 93fdc4bdc83..87398c9785b 100644 --- a/ocaml/asmcomp/cmm_helpers.ml +++ b/ocaml/asmcomp/cmm_helpers.ml @@ -790,12 +790,22 @@ let int_array_ref arr ofs dbg = let unboxed_float_array_ref arr ofs dbg = Cop(Cload (Double, Mutable), [array_indexing log2_size_float arr ofs dbg], dbg) -let float_array_ref arr ofs dbg = - box_float dbg Lambda.alloc_heap (unboxed_float_array_ref arr ofs dbg) +let float_array_ref mode arr ofs dbg = + box_float dbg mode (unboxed_float_array_ref arr ofs dbg) -let addr_array_set arr ofs newval dbg = +let addr_array_set_heap arr ofs newval dbg = Cop(Cextcall("caml_modify", typ_void, [], false), [array_indexing log2_size_addr arr ofs dbg; newval], dbg) + +let addr_array_set_local arr ofs newval dbg = + Cop(Cextcall("caml_modify_local", typ_void, [], false), + [arr; untag_int ofs dbg; newval], dbg) + +let addr_array_set (mode : Lambda.modify_mode) arr ofs newval dbg = + match mode with + | Modify_heap -> addr_array_set_heap arr ofs newval dbg + | Modify_maybe_stack -> addr_array_set_local arr ofs newval dbg +(* int and float arrays can be written to uniformly regardless of their mode *) let int_array_set arr ofs newval dbg = Cop(Cstore (Word_int, Assignment), [array_indexing log2_size_addr arr ofs dbg; newval], dbg) @@ -803,10 +813,6 @@ let float_array_set arr ofs newval dbg = Cop(Cstore (Double, Assignment), [array_indexing log2_size_float arr ofs dbg; newval], dbg) -let addr_array_set_local arr ofs newval dbg = - Cop(Cextcall("caml_modify_local", typ_void, [], false), - [arr; untag_int ofs dbg; newval], dbg) - let addr_array_initialize arr ofs newval dbg = Cop(Cextcall("caml_initialize", typ_void, [], false), [array_indexing log2_size_addr arr ofs dbg; newval], dbg) @@ -2748,28 +2754,28 @@ let bigstring_load size unsafe mode arg1 arg2 dbg = idx (unaligned_load size ba_data idx dbg))))) -let arrayref_unsafe kind arg1 arg2 dbg = - match (kind : Lambda.array_kind) with - | Pgenarray -> +let arrayref_unsafe rkind arg1 arg2 dbg = + match (rkind : Lambda.array_ref_kind) with + | Pgenarray_ref mode -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> Cifthenelse(is_addr_array_ptr arr dbg, dbg, addr_array_ref arr idx dbg, dbg, - float_array_ref arr idx dbg, + float_array_ref mode arr idx dbg, dbg, Any))) - | Paddrarray -> + | Paddrarray_ref -> addr_array_ref arg1 arg2 dbg - | Pintarray -> + | Pintarray_ref -> (* CR mshinwell: for int/addr_array_ref move "dbg" to first arg *) int_array_ref arg1 arg2 dbg - | Pfloatarray -> - float_array_ref arg1 arg2 dbg + | Pfloatarray_ref mode -> + float_array_ref mode arg1 arg2 dbg -let arrayref_safe kind arg1 arg2 dbg = - match (kind : Lambda.array_kind) with - | Pgenarray -> +let arrayref_safe rkind arg1 arg2 dbg = + match (rkind : Lambda.array_ref_kind) with + | Pgenarray_ref mode -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> bind "header" (get_header_without_profinfo arr dbg) (fun hdr -> @@ -2780,7 +2786,7 @@ let arrayref_safe kind arg1 arg2 dbg = dbg, addr_array_ref arr idx dbg, dbg, - float_array_ref arr idx dbg, + float_array_ref mode arr idx dbg, dbg, Any)) else Cifthenelse(is_addr_array_hdr hdr dbg, @@ -2791,34 +2797,34 @@ let arrayref_safe kind arg1 arg2 dbg = dbg, Csequence( make_checkbound dbg [float_array_length_shifted hdr dbg; idx], - float_array_ref arr idx dbg), + float_array_ref mode arr idx dbg), dbg, Any)))) - | Paddrarray -> - bind "index" arg2 (fun idx -> - bind "arr" arg1 (fun arr -> - Csequence( - make_checkbound dbg [ - addr_array_length_shifted - (get_header_without_profinfo arr dbg) dbg; idx], - addr_array_ref arr idx dbg))) - | Pintarray -> - bind "index" arg2 (fun idx -> - bind "arr" arg1 (fun arr -> - Csequence( - make_checkbound dbg [ - addr_array_length_shifted - (get_header_without_profinfo arr dbg) dbg; idx], - int_array_ref arr idx dbg))) - | Pfloatarray -> - box_float dbg Lambda.alloc_heap ( - bind "index" arg2 (fun idx -> - bind "arr" arg1 (fun arr -> - Csequence( - make_checkbound dbg [ - float_array_length_shifted - (get_header_without_profinfo arr dbg) dbg; - idx], - unboxed_float_array_ref arr idx dbg)))) + | Paddrarray_ref -> + bind "index" arg2 (fun idx -> + bind "arr" arg1 (fun arr -> + Csequence( + make_checkbound dbg [ + addr_array_length_shifted + (get_header_without_profinfo arr dbg) dbg; idx], + addr_array_ref arr idx dbg))) + | Pintarray_ref -> + bind "index" arg2 (fun idx -> + bind "arr" arg1 (fun arr -> + Csequence( + make_checkbound dbg [ + addr_array_length_shifted + (get_header_without_profinfo arr dbg) dbg; idx], + int_array_ref arr idx dbg))) + | Pfloatarray_ref mode -> + box_float dbg mode ( + bind "index" arg2 (fun idx -> + bind "arr" arg1 (fun arr -> + Csequence( + make_checkbound dbg [ + float_array_length_shifted + (get_header_without_profinfo arr dbg) dbg; + idx], + unboxed_float_array_ref arr idx dbg)))) type ternary_primitive = expression -> expression -> expression -> Debuginfo.t -> expression @@ -2826,7 +2832,7 @@ type ternary_primitive = let setfield_computed ptr init arg1 arg2 arg3 dbg = match assignment_kind ptr init with | Caml_modify -> - return_unit dbg (addr_array_set arg1 arg2 arg3 dbg) + return_unit dbg (addr_array_set_heap arg1 arg2 arg3 dbg) | Caml_modify_local -> return_unit dbg (addr_array_set_local arg1 arg2 arg3 dbg) | Caml_initialize -> @@ -2850,30 +2856,30 @@ let bytesset_safe arg1 arg2 arg3 dbg = [add_int str idx dbg; newval], dbg)))))) -let arrayset_unsafe kind arg1 arg2 arg3 dbg = - return_unit dbg (match (kind: Lambda.array_kind) with - | Pgenarray -> +let arrayset_unsafe skind arg1 arg2 arg3 dbg = + return_unit dbg (match (skind: Lambda.array_set_kind) with + | Pgenarray_set mode -> bind "newval" arg3 (fun newval -> bind "index" arg2 (fun index -> bind "arr" arg1 (fun arr -> Cifthenelse(is_addr_array_ptr arr dbg, dbg, - addr_array_set arr index newval dbg, + addr_array_set mode arr index newval dbg, dbg, float_array_set arr index (unbox_float dbg newval) dbg, dbg, Any)))) - | Paddrarray -> - addr_array_set arg1 arg2 arg3 dbg - | Pintarray -> + | Paddrarray_set mode -> + addr_array_set mode arg1 arg2 arg3 dbg + | Pintarray_set -> int_array_set arg1 arg2 arg3 dbg - | Pfloatarray -> + | Pfloatarray_set -> float_array_set arg1 arg2 arg3 dbg ) -let arrayset_safe kind arg1 arg2 arg3 dbg = - return_unit dbg (match (kind: Lambda.array_kind) with - | Pgenarray -> +let arrayset_safe skind arg1 arg2 arg3 dbg = + return_unit dbg (match (skind: Lambda.array_set_kind) with + | Pgenarray_set mode -> bind "newval" arg3 (fun newval -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> @@ -2883,7 +2889,7 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = make_checkbound dbg [addr_array_length_shifted hdr dbg; idx], Cifthenelse(is_addr_array_hdr hdr dbg, dbg, - addr_array_set arr idx newval dbg, + addr_array_set mode arr idx newval dbg, dbg, float_array_set arr idx (unbox_float dbg newval) @@ -2895,14 +2901,14 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = dbg, Csequence( make_checkbound dbg [addr_array_length_shifted hdr dbg; idx], - addr_array_set arr idx newval dbg), + addr_array_set mode arr idx newval dbg), dbg, Csequence( make_checkbound dbg [float_array_length_shifted hdr dbg; idx], float_array_set arr idx (unbox_float dbg newval) dbg), dbg, Any))))) - | Paddrarray -> + | Paddrarray_set mode -> bind "newval" arg3 (fun newval -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> @@ -2911,8 +2917,8 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = addr_array_length_shifted (get_header_without_profinfo arr dbg) dbg; idx], - addr_array_set arr idx newval dbg)))) - | Pintarray -> + addr_array_set mode arr idx newval dbg)))) + | Pintarray_set -> bind "newval" arg3 (fun newval -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> @@ -2922,7 +2928,7 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = (get_header_without_profinfo arr dbg) dbg; idx], int_array_set arr idx newval dbg)))) - | Pfloatarray -> + | Pfloatarray_set -> bind_load "newval" arg3 (fun newval -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> diff --git a/ocaml/asmcomp/cmm_helpers.mli b/ocaml/asmcomp/cmm_helpers.mli index c96329ba47f..d3829e2ac9c 100644 --- a/ocaml/asmcomp/cmm_helpers.mli +++ b/ocaml/asmcomp/cmm_helpers.mli @@ -265,11 +265,15 @@ val addr_array_ref : expression -> expression -> Debuginfo.t -> expression val int_array_ref : expression -> expression -> Debuginfo.t -> expression val unboxed_float_array_ref : expression -> expression -> Debuginfo.t -> expression -val float_array_ref : expression -> expression -> Debuginfo.t -> expression -val addr_array_set : +val float_array_ref : + Lambda.alloc_mode -> expression -> expression -> Debuginfo.t -> expression +val addr_array_set_heap : expression -> expression -> expression -> Debuginfo.t -> expression val addr_array_set_local : expression -> expression -> expression -> Debuginfo.t -> expression +val addr_array_set : + Lambda.modify_mode -> expression -> expression -> expression -> Debuginfo.t -> + expression val int_array_set : expression -> expression -> expression -> Debuginfo.t -> expression val float_array_set : @@ -555,9 +559,9 @@ val bigstring_load : (** Arrays *) -(** Array access. Args: array, index *) -val arrayref_unsafe : Lambda.array_kind -> binary_primitive -val arrayref_safe : Lambda.array_kind -> binary_primitive +(** Array access. Args: array, index *) +val arrayref_unsafe : Lambda.array_ref_kind -> binary_primitive +val arrayref_safe : Lambda.array_ref_kind -> binary_primitive type ternary_primitive = expression -> expression -> expression -> Debuginfo.t -> expression @@ -578,8 +582,8 @@ val bytesset_safe : ternary_primitive _unboxed_ float. Otherwise, it is expected to be a regular caml value, including in the case where the array contains floats. Args: array, index, value *) -val arrayset_unsafe : Lambda.array_kind -> ternary_primitive -val arrayset_safe : Lambda.array_kind -> ternary_primitive +val arrayset_unsafe : Lambda.array_set_kind -> ternary_primitive +val arrayset_safe : Lambda.array_set_kind -> ternary_primitive (** Set a chunk of data in the given bytes or bigstring structure. See also [string_load] and [bigstring_load]. diff --git a/ocaml/asmcomp/cmmgen.ml b/ocaml/asmcomp/cmmgen.ml index 03eb963d864..616d86a5152 100644 --- a/ocaml/asmcomp/cmmgen.ml +++ b/ocaml/asmcomp/cmmgen.ml @@ -1067,10 +1067,10 @@ and transl_prim_2 env p arg1 arg2 dbg = bigstring_load size unsafe mode (transl env arg1) (transl env arg2) dbg (* Array operations *) - | Parrayrefu kind -> - arrayref_unsafe kind (transl env arg1) (transl env arg2) dbg - | Parrayrefs kind -> - arrayref_safe kind (transl env arg1) (transl env arg2) dbg + | Parrayrefu rkind -> + arrayref_unsafe rkind (transl env arg1) (transl env arg2) dbg + | Parrayrefs rkind -> + arrayref_safe rkind (transl env arg1) (transl env arg2) dbg (* Boxed integers *) | Paddbint (bi, mode) -> @@ -1155,20 +1155,20 @@ and transl_prim_3 env p arg1 arg2 arg3 dbg = (transl env arg1) (transl env arg2) (transl env arg3) dbg (* Array operations *) - | Parraysetu kind -> + | Parraysetu skind -> let newval = - match kind with - | Pfloatarray -> transl_unbox_float dbg env arg3 + match skind with + | Pfloatarray_set -> transl_unbox_float dbg env arg3 | _ -> transl env arg3 in - arrayset_unsafe kind (transl env arg1) (transl env arg2) newval dbg - | Parraysets kind -> + arrayset_unsafe skind (transl env arg1) (transl env arg2) newval dbg + | Parraysets skind -> let newval = - match kind with - | Pfloatarray -> transl_unbox_float dbg env arg3 + match skind with + | Pfloatarray_set -> transl_unbox_float dbg env arg3 | _ -> transl env arg3 in - arrayset_safe kind (transl env arg1) (transl env arg2) newval dbg + arrayset_safe skind (transl env arg1) (transl env arg2) newval dbg | Pbytes_set(size, unsafe) -> bytes_set size unsafe (transl env arg1) (transl env arg2) diff --git a/ocaml/boot/ocamlc b/ocaml/boot/ocamlc index 42a2331afef77c333b785cb91149c2c184417096..7535aa61de30a4b316c8ffbdde88c84a75482587 100755 GIT binary patch delta 113304 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 z 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

O%fdX*ZAcjSwnx# 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 h}r;7%2Tl)@iVtOw9$-e>+y!ZU;_@7LLsjDw<* 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 Bs$Kv9 delta 10456 zcma)C3tUxI)_0wKP%ucYSGeE>F9Z%68WO$`6^MLgj1a!HgaRLQ(xd# zSAAKdV~=S@NBC$wS(?%`qxKk~vUGeTWe-ymN_#o#_h0972z9;(zu&%l@3q%sueJAH zYwdlwax{G3k?`vIH|n+LLu<=!3?^!?Ya%_mJ~WtIx~5ZH%Wr49-BlO3+!UshL)}EJ zcu@k?rmKrxR=I-p30ENfQRl|Q>FQ3wdJcN-78;>cZ74;8*X9~-V;Ihu{-K4kOAA~m zt9WZEdDe6eq=$4+=OeUCf7UrA>{PpU?RuaPiU;Lxl=ix}J0ZjcPno{b5Noo~srb zZXF!ngU0KA;XYRfNV~7igadU^coxu_e{Z_=&hUun@ff@Tr5I%Mjv8TUt*>#8U^oD`HyrRgfzN3Pv4*T14D~ z(hYt26APG{!ra&h<02DnrQ$-`w~Pv98PP(zHThH$* zR@l@vokG#~VVt~fjMa34g}f%lq#vqBdD9^15$|%kQ3piLj&!CMa_#i=>L?#AIQ=^CHgUI2kO#+TZKQh+i@H~M@bgV2&cp_!{gVrI~0U1O!| zmtz*vSlu%=ksj6KVvpp$gYo_-Vq3v=`aQ4d-+WC!@tS@wr?1OzIuI! zuALQkT7`@SNH&-VTwGg_FvFy0?4uN5|9W(D;%?faYkK*pLO1jpqYHb7Mt1~q7K-R9 zgbTsMX)n^5y*R}VjOKIIiE)Xg_|^qYNer{OxN-)#IY>X#QV98-FLo72dnPxk8pU`YQgx=rWy z9}tv`Ax0wo*3sA3zUtQ>G*Hufr zoWFpAxyN1jsn4Zg+ogupt{!mCg!oH?2k7d-ciOGpl&N-V7FhQoF5%xQF5m&bVYbD3ReXzh+n zj;7(YxziFl(4(~vO#j%VFSKi_kDGQGrVf22kb|qtz}m-V?@@3k+4DBToixw$p8)X> zvIBxq@=)YYtTnebcK!@?EuU5CDzx^^1@%O$xn>AfZ_A=SXf`w%?%JkBhi(YS0qIPg z{P1X9w!DkZ>1^sg4=<%0Zg^UG^*a><*HrYOoZ94yB}6%T>xxg&sd{8@vK-zRAD`$> z5xUZv+D2?`=qq~&?xbNyB7QIJxDUStuX>yQx^{{eRGU04?nrZlhYciMd+2{XfuV zHpa(}0=FJ03dKN?wZ4j!OX9E(ojG1L1k|g9N9eLof_3G|E80NwWT7<%znS0G;%KTWhBfncpL5|cccD;*)?Zo#Hp!xB895CTj+vkVHIF_|9T|7eebbfX( z|5wV8Dk%eU!p2LYy6bWx_zt;TVe?%Hn>55zqF#7qG5OZj5bUrnmPXSc-7v#J*MrK-l|;She%>?!0jTUiAfF&IqeT@j40scYQ%issgN|o1=n0Tl z88iaS4jXjDpG4xQ1j^F(aCmnyDaKBQB>Xv{KwM+1d*ob`JT!tEW|>|NEmtNuAzil( z^s~U1O!7if&6kExZ}ge=Qp9m^04+q?TOI%o_w!Q$P<_1hZUDXC5nEK8y3cm%KHI5F z!qwKP`)sE^=znnPpodeJ#O~iY^+DEI7s0C++Bcgb`**tiJH!5+X>IC4c`D#O(D&fj z2=a252--qE|4{(*Qz=$pB#k9IcXW0t;O8p%OjjCvHQ2&-?L?r)N!bgfu=hD9nsx#b z(v7B}vX6<6xB7eb5%HtV^wFIy6eoO;=d%xr{Q7j|OQz0`GOfxOiW2TyV=0abxgi!u z&S$x9q5%f*>n&=l>q|En#!v0|oBs42 zz^4XKBC3W$$lUO}3g(P~6hW?aF4M#F2hv82xl(BWs!^%f9xh~c9&XB4CYPnsn?aIq zi+69&v2S9g`GaVzvVnjhkI1~!&Gkd5666I#v4LFtz)-5_9VUY)vh0$tNk0Qc{^Y<4 z>?ah-ood)v zep$n5l`Xy#XV$=t^cq6Fc;g5f8zkIB(Gafr19{gxZ?^Xvjc*!B!@+FUNVpAKtv`Wh z?D=%wFp4Gy*vjkX;a)zJgj{Fvn}`s7?x6UVZKa$3X!4vpXrj$O1N_@@%`pi6@(!9G zEiz@B-iu)H(y*d^w398v%1H9fQ*x#+TwLNyl7?utIa_Wy1T2>oG5% z$fc1;PkY{lwLY}ejNm76d!U~Xv*NhAGjy-F`&^PvDIA(dE85Hm2+& zdjHi@??f%8awvq_G3RbRHj$?I@4wu)hz7cLV^pHf&lq)+D35A5J0A!ARk@o_qnEjA zGAviajRLIUOZn*4an&=1o0}$6L_i%lEb#YZrcfxo!O2sAxtR+J$VV0zOK&%?mTIf@ z{1m!@XopoZm9|kxJ!qY|9=}ezV;GgieygQEg1W7CLha1`_;uPH$7oyaj+XqiYKB@rg9K z?NTBexk}j-URH#q?9L5En8hu4J@9Z@K4k>$fDDn7pbOk?&Yw=E(Vh5@lc_6^%cqma z=m+kTshh3z&gs}|19<;*F!AdbeKRDrHGyoopH+&T0nD3t$P6l#M>hLS<&CQ__RoOQ zy{xk{$b*X!m;1~(?lBujU}#l|n{#F(s~P%p@@j3EO>+<*t)RJxhOY5oH3voR5&j%$ z(mYB-8nkX6Qn7a2I1jN5$?JUhQ9Fm8hg2oG1mTYt55+D2Uo%xaAN@7L(I75fKnJMF z8nTc&C}S$|KMGYhtFROSFJLfcKAtySA`izrKpQCA+Vuc+FoNWXLblW}ZeD_9dhnt^ z>&z0`XWT4aNBp9*Led|IvKK}E#9xZl5p|D0MB#|O)^`ulARKc}E`!A$YWj@XDHnzrFZr~sGu1185@MjnjUzsp#iB7WvFZN^e&UZLM9BpZ!}F$L=| zL>XGL9%ZbgLDnSGSVCB|g8@bYZamhd_C^QPNVUBj6KM2Bdfq(Av*vU#AI+gCYi*!m z3i?w)2A(Mg^ZsFmm+NmgexSh?+DV;I|2WpbTlioO8E=e1_wMn=iwZ9ut~o}M)pxS- zL0{+*^n?*7di1U|o8*dBCQ+xdNU;b*0K%X!D z&B#NA2h+_)5%E1<70$tXjfFrh-)mTcB9Ez@|CYcQu*B_e8;2;M2%EDbm;Xcj@;gQj z#xmbE_JSf=qY$&2>m-~47{ z1+C%>&CqHUmwaO+A`&FeRB^oL8zYnwxZxWp`f?l4YH7nsxsm+ld<&eBT=1uB7>1>->3RVtcW{#nxHtIm?5&vMXMpc3w2P zxa2}4?!?KC<(wH=6fH|!0r+B+KooHfPKJZ!zGS4k1Vxf&@kEl>j^wIK#t|B66<;>+ zF5t5sxdNMoIGhrJ;#$|B+^vw_fCRNdB(se{i4};=+;MY$=Hp1oDjy`}Yvmxb3*ts< zVJa*p3JV)_{RFLHh8h#vVnd`zU4mqC;xWBk?ox?db|*!=80uEQVON~m468TZkF5V+ zgOID$$Eg^&`sO$_2A8GGcy%8tR};K*eu-)AiC6h@E@OMDDX5n9RB@o*)KmQlK_>fV z=~*Q=tB(}i=crp$RuFCxn|`l6)^h{Y2M9a-z#x@?J-%s>!dWTix-_*C&{2cc(?POG z^DxU!?8;E??L$C-kixNgX##)=D za&Bnd_7X~E+t-1+6IP2Y8lzq$%0Sva@%vXnqHt;j|Vk?*&eV^KEzjd$RWpi`1_&Bb*34lFCYNUbD;L7YXmSW+@bHQTeLqm1R`Iy{{8cu=hcQ@rP71ziHT z2a4jxOK`bSOH@Q>=iEAn?@~K!_!9LiJhfXjOVtyKc5r5yitFhlVUjz^>xhstNKj@OC-&dmY0eBu4zdsR5#Wh>S9eEziRAy>@M^}jPsu3n|S1nX3{7==ZMeNc4 z#HsbqVATc{#W_!_GXLzxpo-l9zn^23Y>?yLnK%7W&EqYvsZh?@p#s-G3(^-j^;wli zFG$E=3;tW4#X`KmJD!D~e!*&dR%I!_&I_z3f3FIqgPB!WWiO~07|R07ZDO?CZECr# zO)!@{XL_tN+f-nHoyfne+7nmy!x}6dN`ZRkbAcSX9YQ*D^mdglDEDkv_bM#Ou5)su z7>zXH#16H`Z>J}0rZc&@3HSf9m3Z)f1j)r~>K&@!=$$H^Dy+MAstFx}R$(W|4qIUD z`m2N!`}5*FzVmPDN%*AfzpFHk`8&*Jx8=2R>)+wyi#T$x8h~EzURbJ#H|Md=8&3wjp&;9mB)SsZbgQ!t!-|6UyTST z1jx-Kce2XEu;85^z{EbD^npqXb8^Ns$dNz4yqDSzCHO~LhJM5l!g=FioKb}HgVM9Z zo{yh8XluGds@C3*{$AvV_E_qNDhX=O`Va?TuGRPih5Y?Ri*vZGFYp z*^`BGt!=*hFvJw{vcq;N@%-VobG=~q+RW9?mgay@5@&vBCoAjzsSXh6i;jv4)+0v| zm(dM4Zfn&2xWbFQEYdvaPyn>W$7xcnjH=E%G9qi57Z>&M|Z1fNwKkyvd#s}9IgE&Iu@k5onMx`;=`>VAgF z8=sL+y-mUEFCxXW2WnmEFi3wWHMQlUpKcwzs17JOOfOtfZxHSdv+xNXYj}xFxjRG} z=FfMBTR|yF)SR|++?6b|?l zJiLq?XZ8i#*a)me#wx?KHV6PyYuJJSS@s(<_{FC2xM1g4VDr90>N#tgxsCI;A<>JO zVj{FmHD8g$^jLwJ$B*4(9>t`9vcNzMSWO0d!BKHNm3UTTlwPwvW z-|u+0ymiR#y@EeE@e*5{_YWr%aQaR(cKXh>(CIr@A*b)MKODS#zd05LEhxp6A*$3A zzudFb9N-7}P?eXOS<&*iEPuD7$TtD$%Y5XTgNRUy>@R;Pnd=vu5eN#Ai_K|9xFbjA zk2i{zx$}QKXwC)OOAq4OCjnVkeidp>U1Cm@2=$kxW*jQ}e_`e{2lBs`nkTWM{K+!& zUa5yaWd2g>^$(fdq~89J+28)AAqjv8YG*ov+O^B%f;?yyvD_RSG#5j1Z|=ak^~z^0 zU2aYcgt0g6Cd8_h=ENZ6w!^B-^wt9{)|EY9d)1!bRAnBx5vxXvU;07Y-!sP@HGF^I34adg@!#B&#E}31 diff --git a/ocaml/bytecomp/bytegen.ml b/ocaml/bytecomp/bytegen.ml index d2268155244..c3130582a5f 100644 --- a/ocaml/bytecomp/bytegen.ml +++ b/ocaml/bytecomp/bytegen.ml @@ -454,18 +454,23 @@ let comp_primitive p args = | Pbytes_load_32(_) -> Kccall("caml_bytes_get32", 2) | Pbytes_load_64(_) -> Kccall("caml_bytes_get64", 2) | Parraylength _ -> Kvectlength - | Parrayrefs Pgenarray -> Kccall("caml_array_get", 2) - | Parrayrefs Pfloatarray -> Kccall("caml_floatarray_get", 2) - | Parrayrefs _ -> Kccall("caml_array_get_addr", 2) - | Parraysets Pgenarray -> Kccall("caml_array_set", 3) - | Parraysets Pfloatarray -> Kccall("caml_floatarray_set", 3) - | Parraysets _ -> Kccall("caml_array_set_addr", 3) - | Parrayrefu Pgenarray -> Kccall("caml_array_unsafe_get", 2) - | Parrayrefu Pfloatarray -> Kccall("caml_floatarray_unsafe_get", 2) - | Parrayrefu _ -> Kgetvectitem - | Parraysetu Pgenarray -> Kccall("caml_array_unsafe_set", 3) - | Parraysetu Pfloatarray -> Kccall("caml_floatarray_unsafe_set", 3) - | Parraysetu _ -> Ksetvectitem + (* In bytecode, nothing is ever actually stack-allocated, so we ignore the + array modes (allocation for [Parrayref{s,u}], modification for + [Parrayset{s,u}]). *) + | Parrayrefs (Pgenarray_ref _) -> Kccall("caml_array_get", 2) + | Parrayrefs (Pfloatarray_ref _) -> Kccall("caml_floatarray_get", 2) + | Parrayrefs (Paddrarray_ref | Pintarray_ref) -> + Kccall("caml_array_get_addr", 2) + | Parraysets (Pgenarray_set _) -> Kccall("caml_array_set", 3) + | Parraysets Pfloatarray_set -> Kccall("caml_floatarray_set", 3) + | Parraysets (Paddrarray_set _ | Pintarray_set) -> + Kccall("caml_array_set_addr", 3) + | Parrayrefu (Pgenarray_ref _) -> Kccall("caml_array_unsafe_get", 2) + | Parrayrefu (Pfloatarray_ref _) -> Kccall("caml_floatarray_unsafe_get", 2) + | Parrayrefu (Paddrarray_ref | Pintarray_ref) -> Kgetvectitem + | Parraysetu (Pgenarray_set _) -> Kccall("caml_array_unsafe_set", 3) + | Parraysetu Pfloatarray_set -> Kccall("caml_floatarray_unsafe_set", 3) + | Parraysetu (Paddrarray_set _ | Pintarray_set) -> Ksetvectitem | Pctconst c -> let const_name = match c with | Big_endian -> "big_endian" diff --git a/ocaml/lambda/lambda.ml b/ocaml/lambda/lambda.ml index 09029793cbc..adc30eec738 100644 --- a/ocaml/lambda/lambda.ml +++ b/ocaml/lambda/lambda.ml @@ -176,10 +176,10 @@ type primitive = | Pmakearray of array_kind * mutable_flag * alloc_mode | Pduparray of array_kind * mutable_flag | Parraylength of array_kind - | Parrayrefu of array_kind - | Parraysetu of array_kind - | Parrayrefs of array_kind - | Parraysets of array_kind + | Parrayrefu of array_ref_kind + | Parraysetu of array_set_kind + | Parrayrefs of array_ref_kind + | Parraysets of array_set_kind (* Test if the argument is a block or an immediate integer *) | Pisint of { variant_only : bool } (* Test if the (integer) argument is outside an interval *) @@ -274,6 +274,18 @@ and block_shape = and array_kind = Pgenarray | Paddrarray | Pintarray | Pfloatarray +and array_ref_kind = + | Pgenarray_ref of alloc_mode + | Paddrarray_ref + | Pintarray_ref + | Pfloatarray_ref of alloc_mode + +and array_set_kind = + | Pgenarray_set of modify_mode + | Paddrarray_set of modify_mode + | Pintarray_set + | Pfloatarray_set + and boxed_integer = Primitive.boxed_integer = Pnativeint | Pint32 | Pint64 @@ -1355,12 +1367,10 @@ let primitive_may_allocate : primitive -> alloc_mode option = function | Pduparray _ -> Some alloc_heap | Parraylength _ -> None | Parraysetu _ | Parraysets _ - | Parrayrefu (Paddrarray|Pintarray) - | Parrayrefs (Paddrarray|Pintarray) -> None - | Parrayrefu (Pgenarray|Pfloatarray) - | Parrayrefs (Pgenarray|Pfloatarray) -> - (* The float box from flat floatarray access is always Alloc_heap *) - Some alloc_heap + | Parrayrefu (Paddrarray_ref | Pintarray_ref) + | Parrayrefs (Paddrarray_ref | Pintarray_ref) -> None + | Parrayrefu (Pgenarray_ref m | Pfloatarray_ref m) + | Parrayrefs (Pgenarray_ref m | Pfloatarray_ref m) -> Some m | Pisint _ | Pisout -> None | Pintofbint _ -> None | Pbintofint (_,m) @@ -1449,11 +1459,11 @@ let primitive_result_layout (p : primitive) = | Pstring_load_16 _ | Pbytes_load_16 _ | Pbigstring_load_16 _ | Pprobe_is_enabled _ | Pbswap16 -> layout_int - | Parrayrefu array_kind | Parrayrefs array_kind -> - (match array_kind with - | Pintarray -> layout_int - | Pfloatarray -> layout_float - | Pgenarray | Paddrarray -> layout_field) + | Parrayrefu array_ref_kind | Parrayrefs array_ref_kind -> + (match array_ref_kind with + | Pintarray_ref -> layout_int + | Pfloatarray_ref _ -> layout_float + | Pgenarray_ref _ | Paddrarray_ref -> layout_field) | Pbintofint (bi, _) | Pcvtbint (_,bi,_) | Pnegbint (bi, _) | Paddbint (bi, _) | Psubbint (bi, _) | Pmulbint (bi, _) | Pdivbint {size = bi} | Pmodbint {size = bi} @@ -1528,3 +1538,15 @@ let compute_expr_layout free_vars_kind lam = | Lexclave e -> compute_expr_layout kinds e in compute_expr_layout Ident.Map.empty lam + +let array_ref_kind mode = function + | Pgenarray -> Pgenarray_ref mode + | Paddrarray -> Paddrarray_ref + | Pintarray -> Pintarray_ref + | Pfloatarray -> Pfloatarray_ref mode + +let array_set_kind mode = function + | Pgenarray -> Pgenarray_set mode + | Paddrarray -> Paddrarray_set mode + | Pintarray -> Pintarray_set + | Pfloatarray -> Pfloatarray_set diff --git a/ocaml/lambda/lambda.mli b/ocaml/lambda/lambda.mli index f368fae0720..cf4809d63fe 100644 --- a/ocaml/lambda/lambda.mli +++ b/ocaml/lambda/lambda.mli @@ -127,10 +127,10 @@ type primitive = The arguments of [Pduparray] give the kind and mutability of the array being *produced* by the duplication. *) | Parraylength of array_kind - | Parrayrefu of array_kind - | Parraysetu of array_kind - | Parrayrefs of array_kind - | Parraysets of array_kind + | Parrayrefu of array_ref_kind + | Parraysetu of array_set_kind + | Parrayrefs of array_ref_kind + | Parraysets of array_set_kind (* Test if the argument is a block or an immediate integer *) | Pisint of { variant_only : bool } (* Test if the (integer) argument is outside an interval *) @@ -209,6 +209,22 @@ and float_comparison = and array_kind = Pgenarray | Paddrarray | Pintarray | Pfloatarray +(** When accessing a flat float array, we need to know the mode which we should + box the resulting float at. *) +and array_ref_kind = + | Pgenarray_ref of alloc_mode (* This might be a flat float array *) + | Paddrarray_ref + | Pintarray_ref + | Pfloatarray_ref of alloc_mode + +(** When updating an array that might contain pointers, we need to know what + mode they're at; otherwise, access is uniform. *) +and array_set_kind = + | Pgenarray_set of modify_mode (* This might be an array of pointers *) + | Paddrarray_set of modify_mode + | Pintarray_set + | Pfloatarray_set + and value_kind = Pgenval | Pfloatval | Pboxedintval of boxed_integer | Pintval | Pvariant of { @@ -670,3 +686,9 @@ val structured_constant_layout : structured_constant -> layout val primitive_result_layout : primitive -> layout val compute_expr_layout : (Ident.t -> layout option) -> lambda -> layout + +(** The mode will be discarded if unnecessary for the given [array_kind] *) +val array_ref_kind : alloc_mode -> array_kind -> array_ref_kind + +(** The mode will be discarded if unnecessary for the given [array_kind] *) +val array_set_kind : modify_mode -> array_kind -> array_set_kind diff --git a/ocaml/lambda/matching.ml b/ocaml/lambda/matching.ml index 13ef33d0fa3..ff0208bfc24 100644 --- a/ocaml/lambda/matching.ml +++ b/ocaml/lambda/matching.ml @@ -2174,7 +2174,11 @@ let get_expr_args_array ~scopes kind head (arg, _mut, _layout) rem = else (* CR ncourant: could do better than layout_field using kind *) ( Lprim - (Parrayrefu kind, [ arg; Lconst (Const_base (Const_int pos)) ], loc), + (* TODO: The resulting float should be allocated to at the mode of the + array pattern, once that's available *) + (Parrayrefu Lambda.(array_ref_kind alloc_heap kind), + [ arg; Lconst (Const_base (Const_int pos)) ], + loc), (match am with | Mutable -> StrictOpt | Immutable -> Alias), diff --git a/ocaml/lambda/printlambda.ml b/ocaml/lambda/printlambda.ml index 3cb44d1579c..9f6b1aaac15 100644 --- a/ocaml/lambda/printlambda.ml +++ b/ocaml/lambda/printlambda.ml @@ -54,6 +54,28 @@ let array_kind = function | Pintarray -> "int" | Pfloatarray -> "float" +let array_ref_kind ppf k = + let pp_mode ppf = function + | Alloc_heap -> () + | Alloc_local -> fprintf ppf "(local)" + in + match k with + | Pgenarray_ref mode -> fprintf ppf "gen%a" pp_mode mode + | Paddrarray_ref -> fprintf ppf "addr" + | Pintarray_ref -> fprintf ppf "int" + | Pfloatarray_ref mode -> fprintf ppf "float%a" pp_mode mode + +let array_set_kind ppf k = + let pp_mode ppf = function + | Modify_heap -> () + | Modify_maybe_stack -> fprintf ppf "(local)" + in + match k with + | Pgenarray_set mode -> fprintf ppf "gen%a" pp_mode mode + | Paddrarray_set mode -> fprintf ppf "addr%a" pp_mode mode + | Pintarray_set -> fprintf ppf "int" + | Pfloatarray_set -> fprintf ppf "float" + let alloc_mode = function | Alloc_heap -> "" | Alloc_local -> "local" @@ -345,10 +367,10 @@ let primitive ppf = function | Pduparray (k, Immutable) -> fprintf ppf "duparray_imm[%s]" (array_kind k) | Pduparray (k, Immutable_unique) -> fprintf ppf "duparray_unique[%s]" (array_kind k) - | Parrayrefu k -> fprintf ppf "array.unsafe_get[%s]" (array_kind k) - | Parraysetu k -> fprintf ppf "array.unsafe_set[%s]" (array_kind k) - | Parrayrefs k -> fprintf ppf "array.get[%s]" (array_kind k) - | Parraysets k -> fprintf ppf "array.set[%s]" (array_kind k) + | Parrayrefu rk -> fprintf ppf "array.unsafe_get[%a]" array_ref_kind rk + | Parraysetu sk -> fprintf ppf "array.unsafe_set[%a]" array_set_kind sk + | Parrayrefs rk -> fprintf ppf "array.get[%a]" array_ref_kind rk + | Parraysets sk -> fprintf ppf "array.set[%a]" array_set_kind sk | Pctconst c -> let const_name = match c with | Big_endian -> "big_endian" diff --git a/ocaml/lambda/transl_array_comprehension.ml b/ocaml/lambda/transl_array_comprehension.ml index dda421bd072..4277e8e4a46 100644 --- a/ocaml/lambda/transl_array_comprehension.ml +++ b/ocaml/lambda/transl_array_comprehension.ml @@ -495,7 +495,8 @@ let iterator ~transl_exp ~scopes ~loc Matching.for_let ~scopes pattern.pat_loc - (Lprim(Parrayrefu iter_arr_kind, + (Lprim(Parrayrefu + Lambda.(array_ref_kind alloc_heap iter_arr_kind), [iter_arr.var; Lvar iter_ix], loc)) pattern @@ -756,7 +757,9 @@ let body let open Let_binding in let set_element_raw elt = (* array.(index) <- elt *) - Lprim(Parraysetu array_kind, [array.var; index.var; elt], loc) + Lprim(Parraysetu Lambda.(array_set_kind modify_heap array_kind), + [array.var; index.var; elt], + loc) in let set_element_in_bounds elt = match array_sizing with | Fixed_size -> diff --git a/ocaml/lambda/translprim.ml b/ocaml/lambda/translprim.ml index 195f2e9f832..9b5527e4fc8 100644 --- a/ocaml/lambda/translprim.ml +++ b/ocaml/lambda/translprim.ml @@ -114,6 +114,12 @@ let get_units_with_used_primitives () = let gen_array_kind = if Config.flat_float_array then Pgenarray else Paddrarray +let gen_array_ref_kind mode = + if Config.flat_float_array then Pgenarray_ref mode else Paddrarray_ref + +let gen_array_set_kind mode = + if Config.flat_float_array then Pgenarray_set mode else Paddrarray_set mode + let prim_sys_argv = Primitive.simple ~name:"caml_sys_argv" ~arity:1 ~alloc:true @@ -136,6 +142,13 @@ let to_modify_mode ~poly = function let lookup_primitive loc poly pos p = let mode = to_alloc_mode ~poly p.prim_native_repr_res in let arg_modes = List.map (to_modify_mode ~poly) p.prim_native_repr_args in + let get_first_arg_mode () = + match arg_modes with + | mode :: _ -> mode + | [] -> + Misc.fatal_errorf "Primitive \"%s\" unexpectedly had zero arguments" + p.prim_name + in let prim = match p.prim_name with | "%identity" -> Identity | "%bytes_to_string" -> Primitive (Pbytes_to_string, 1) @@ -154,12 +167,8 @@ let lookup_primitive loc poly pos p = | "%field0_immut" -> Primitive ((Pfield (0, Reads_agree)), 1) | "%field1_immut" -> Primitive ((Pfield (1, Reads_agree)), 1) | "%setfield0" -> - let mode = - match arg_modes with - | mode :: _ -> Assignment mode - | [] -> assert false - in - Primitive ((Psetfield(0, Pointer, mode)), 2) + let mode = get_first_arg_mode () in + Primitive ((Psetfield(0, Pointer, Assignment mode)), 2) | "%makeblock" -> Primitive ((Pmakeblock(0, Immutable, None, mode)), 1) | "%makemutable" -> Primitive ((Pmakeblock(0, Mutable, None, mode)), 1) | "%raise" -> Raise Raise_regular @@ -225,18 +234,23 @@ let lookup_primitive loc poly pos p = | "%bytes_unsafe_get" -> Primitive (Pbytesrefu, 2) | "%bytes_unsafe_set" -> Primitive (Pbytessetu, 3) | "%array_length" -> Primitive ((Parraylength gen_array_kind), 1) - | "%array_safe_get" -> Primitive ((Parrayrefs gen_array_kind), 2) - | "%array_safe_set" -> Primitive ((Parraysets gen_array_kind), 3) - | "%array_unsafe_get" -> Primitive ((Parrayrefu gen_array_kind), 2) - | "%array_unsafe_set" -> Primitive ((Parraysetu gen_array_kind), 3) - | "%obj_size" -> Primitive ((Parraylength gen_array_kind), 1) - | "%obj_field" -> Primitive ((Parrayrefu gen_array_kind), 2) - | "%obj_set_field" -> Primitive ((Parraysetu gen_array_kind), 3) + | "%array_safe_get" -> Primitive ((Parrayrefs (gen_array_ref_kind mode)), 2) + | "%array_safe_set" -> + Primitive (Parraysets (gen_array_set_kind (get_first_arg_mode ())), 3) + | "%array_unsafe_get" -> Primitive (Parrayrefu (gen_array_ref_kind mode), 2) + | "%array_unsafe_set" -> + Primitive ((Parraysetu (gen_array_set_kind (get_first_arg_mode ()))), 3) + | "%obj_size" -> Primitive ((Parraylength Pgenarray), 1) + | "%obj_field" -> Primitive ((Parrayrefu (Pgenarray_ref mode)), 2) + | "%obj_set_field" -> + Primitive ((Parraysetu (Pgenarray_set (get_first_arg_mode ()))), 3) | "%floatarray_length" -> Primitive ((Parraylength Pfloatarray), 1) - | "%floatarray_safe_get" -> Primitive ((Parrayrefs Pfloatarray), 2) - | "%floatarray_safe_set" -> Primitive ((Parraysets Pfloatarray), 3) - | "%floatarray_unsafe_get" -> Primitive ((Parrayrefu Pfloatarray), 2) - | "%floatarray_unsafe_set" -> Primitive ((Parraysetu Pfloatarray), 3) + | "%floatarray_safe_get" -> + Primitive ((Parrayrefs (Pfloatarray_ref mode)), 2) + | "%floatarray_safe_set" -> Primitive (Parraysets Pfloatarray_set, 3) + | "%floatarray_unsafe_get" -> + Primitive ((Parrayrefu (Pfloatarray_ref mode)), 2) + | "%floatarray_unsafe_set" -> Primitive ((Parraysetu Pfloatarray_set), 3) | "%obj_is_int" -> Primitive (Pisint { variant_only = false }, 1) | "%lazy_force" -> Lazy_force pos | "%nativeint_of_int" -> Primitive ((Pbintofint (Pnativeint, mode)), 1) @@ -435,14 +449,68 @@ let simplify_constant_constructor = function *) let glb_array_type t1 t2 = match t1, t2 with + (* No GLB; only used in the [Obj.magic] case *) | Pfloatarray, (Paddrarray | Pintarray) | (Paddrarray | Pintarray), Pfloatarray -> t1 + (* Compute the correct GLB *) | Pgenarray, x | x, Pgenarray -> x | Paddrarray, x | x, Paddrarray -> x | Pintarray, Pintarray -> Pintarray | Pfloatarray, Pfloatarray -> Pfloatarray +let glb_array_ref_type t1 t2 = + match t1, t2 with + (* No GLB; only used in the [Obj.magic] case *) + | Pfloatarray_ref _, (Paddrarray | Pintarray) + | (Paddrarray_ref | Pintarray_ref), Pfloatarray -> t1 + + (* Compute the correct GLB *) + + (* Pgenarray >= _ *) + | (Pgenarray_ref _ as x), Pgenarray -> x + | Pgenarray_ref _, Pintarray -> Pintarray_ref + | Pgenarray_ref _, Paddrarray -> Paddrarray_ref + | Pgenarray_ref mode, Pfloatarray -> Pfloatarray_ref mode + | (Paddrarray_ref | Pintarray_ref | Pfloatarray_ref _ as x), Pgenarray -> x + + (* Paddrarray > Pintarray *) + | Paddrarray_ref, Paddrarray -> Paddrarray_ref + | Paddrarray_ref, Pintarray -> Pintarray_ref + | Pintarray_ref, Paddrarray -> Pintarray_ref + + (* Pintarray is a minimum *) + | Pintarray_ref, Pintarray -> Pintarray_ref + + (* Pfloatarray is a minimum *) + | (Pfloatarray_ref _ as x), Pfloatarray -> x + +let glb_array_set_type t1 t2 = + match t1, t2 with + (* No GLB; only used in the [Obj.magic] case *) + | Pfloatarray_set, (Paddrarray | Pintarray) + | (Paddrarray_set _ | Pintarray_set), Pfloatarray -> t1 + + (* Compute the correct GLB *) + + (* Pgenarray >= _ *) + | (Pgenarray_set _ as x), Pgenarray -> x + | Pgenarray_set _, Pintarray -> Pintarray_set + | Pgenarray_set mode, Paddrarray -> Paddrarray_set mode + | Pgenarray_set _, Pfloatarray -> Pfloatarray_set + | (Paddrarray_set _ | Pintarray_set | Pfloatarray_set as x), Pgenarray -> x + + (* Paddrarray > Pintarray *) + | (Paddrarray_set _ as x), Paddrarray -> x + | Paddrarray_set _, Pintarray -> Pintarray_set + | Pintarray_set, Paddrarray -> Pintarray_set + + (* Pintarray is a minimum *) + | Pintarray_set, Pintarray -> Pintarray_set + + (* Pfloatarray is a minimum *) + | Pfloatarray_set, Pfloatarray -> Pfloatarray_set + (* Specialize a primitive from available type information. *) (* CR layouts v2: This function had a loc argument added just to support the void check error message. Take it out when we remove that. *) @@ -466,25 +534,26 @@ let specialize_primitive env loc ty ~has_constant_constructor prim = if t = array_type then None else Some (Primitive (Parraylength array_type, arity)) end - | Primitive (Parrayrefu t, arity), p1 :: _ -> begin - let array_type = glb_array_type t (array_type_kind env p1) in - if t = array_type then None - else Some (Primitive (Parrayrefu array_type, arity)) + | Primitive (Parrayrefu rt, arity), p1 :: _ -> begin + let array_ref_type = glb_array_ref_type rt (array_type_kind env p1) + in + if rt = array_ref_type then None + else Some (Primitive (Parrayrefu array_ref_type, arity)) end - | Primitive (Parraysetu t, arity), p1 :: _ -> begin - let array_type = glb_array_type t (array_type_kind env p1) in - if t = array_type then None - else Some (Primitive (Parraysetu array_type, arity)) + | Primitive (Parraysetu st, arity), p1 :: _ -> begin + let array_set_type = glb_array_set_type st (array_type_kind env p1) in + if st = array_set_type then None + else Some (Primitive (Parraysetu array_set_type, arity)) end - | Primitive (Parrayrefs t, arity), p1 :: _ -> begin - let array_type = glb_array_type t (array_type_kind env p1) in - if t = array_type then None - else Some (Primitive (Parrayrefs array_type, arity)) + | Primitive (Parrayrefs rt, arity), p1 :: _ -> begin + let array_ref_type = glb_array_ref_type rt (array_type_kind env p1) in + if rt = array_ref_type then None + else Some (Primitive (Parrayrefs array_ref_type, arity)) end - | Primitive (Parraysets t, arity), p1 :: _ -> begin - let array_type = glb_array_type t (array_type_kind env p1) in - if t = array_type then None - else Some (Primitive (Parraysets array_type, arity)) + | Primitive (Parraysets st, arity), p1 :: _ -> begin + let array_set_type = glb_array_set_type st (array_type_kind env p1) in + if st = array_set_type then None + else Some (Primitive (Parraysets array_set_type, arity)) end | Primitive (Pbigarrayref(unsafe, n, Pbigarray_unknown, Pbigarray_unknown_layout), arity), p1 :: _ -> begin @@ -866,7 +935,10 @@ let lambda_primitive_needs_event_after = function | Paddfloat _ | Psubfloat _ | Pmulfloat _ | Pdivfloat _ | Pstringrefs | Pbytesrefs | Pbox_float _ | Pbox_int _ | Pbytessets | Pmakearray (Pgenarray, _, _) | Pduparray _ - | Parrayrefu (Pgenarray | Pfloatarray) | Parraysetu (Pgenarray | Pfloatarray) + | Parrayrefu (Pgenarray_ref _ | Pfloatarray_ref _) + | Parraysetu (Pgenarray_set _ | Pfloatarray_set) + (* CR aspectorzabusky for mshinwell: It seems to be that unsafe set should + never allocate or throw an exception; am I missing something? *) | Parrayrefs _ | Parraysets _ | Pbintofint _ | Pcvtbint _ | Pnegbint _ | Paddbint _ | Psubbint _ | Pmulbint _ | Pdivbint _ | Pmodbint _ | Pandbint _ | Porbint _ | Pxorbint _ | Plslbint _ | Plsrbint _ | Pasrbint _ | Pbintcomp _ diff --git a/ocaml/middle_end/clambda_primitives.ml b/ocaml/middle_end/clambda_primitives.ml index 77e66745305..92725a7a771 100644 --- a/ocaml/middle_end/clambda_primitives.ml +++ b/ocaml/middle_end/clambda_primitives.ml @@ -32,6 +32,8 @@ type memory_access_size = type alloc_mode = Lambda.alloc_mode +type modify_mode = Lambda.modify_mode + type primitive = | Pread_symbol of string (* Operations on heap blocks *) @@ -74,10 +76,10 @@ type primitive = The arguments of [Pduparray] give the kind and mutability of the array being *produced* by the duplication. *) | Parraylength of array_kind - | Parrayrefu of array_kind - | Parraysetu of array_kind - | Parrayrefs of array_kind - | Parraysets of array_kind + | Parrayrefu of array_ref_kind + | Parraysetu of array_set_kind + | Parrayrefs of array_ref_kind + | Parraysets of array_set_kind (* Test if the argument is a block or an immediate integer *) | Pisint (* Test if the (integer) argument is outside an interval *) @@ -136,6 +138,18 @@ and float_comparison = Lambda.float_comparison = and array_kind = Lambda.array_kind = Pgenarray | Paddrarray | Pintarray | Pfloatarray +and array_ref_kind = Lambda.array_ref_kind = + | Pgenarray_ref of alloc_mode + | Paddrarray_ref + | Pintarray_ref + | Pfloatarray_ref of alloc_mode + +and array_set_kind = Lambda.array_set_kind = + | Pgenarray_set of modify_mode + | Paddrarray_set of modify_mode + | Pintarray_set + | Pfloatarray_set + and value_kind = Lambda.value_kind = (* CR mshinwell: Pfloatval should be renamed to Pboxedfloatval *) Pgenval | Pfloatval | Pboxedintval of boxed_integer | Pintval diff --git a/ocaml/middle_end/clambda_primitives.mli b/ocaml/middle_end/clambda_primitives.mli index e71b17c4c85..b65e674ee76 100644 --- a/ocaml/middle_end/clambda_primitives.mli +++ b/ocaml/middle_end/clambda_primitives.mli @@ -32,6 +32,8 @@ type memory_access_size = type alloc_mode = Lambda.alloc_mode +type modify_mode = Lambda.modify_mode + type primitive = | Pread_symbol of string (* Operations on heap blocks *) @@ -77,10 +79,10 @@ type primitive = The arguments of [Pduparray] give the kind and mutability of the array being *produced* by the duplication. *) | Parraylength of array_kind - | Parrayrefu of array_kind - | Parraysetu of array_kind - | Parrayrefs of array_kind - | Parraysets of array_kind + | Parrayrefu of array_ref_kind + | Parraysetu of array_set_kind + | Parrayrefs of array_ref_kind + | Parraysets of array_set_kind (* Test if the argument is a block or an immediate integer *) | Pisint (* Test if the (integer) argument is outside an interval *) @@ -139,6 +141,18 @@ and float_comparison = Lambda.float_comparison = and array_kind = Lambda.array_kind = Pgenarray | Paddrarray | Pintarray | Pfloatarray +and array_ref_kind = Lambda.array_ref_kind = + | Pgenarray_ref of alloc_mode + | Paddrarray_ref + | Pintarray_ref + | Pfloatarray_ref of alloc_mode + +and array_set_kind = Lambda.array_set_kind = + | Pgenarray_set of modify_mode + | Paddrarray_set of modify_mode + | Pintarray_set + | Pfloatarray_set + and value_kind = Lambda.value_kind = (* CR mshinwell: Pfloatval should be renamed to Pboxedfloatval *) Pgenval | Pfloatval | Pboxedintval of boxed_integer | Pintval diff --git a/ocaml/middle_end/closure/closure.ml b/ocaml/middle_end/closure/closure.ml index a4c352d03fc..fd9b3615375 100644 --- a/ocaml/middle_end/closure/closure.ml +++ b/ocaml/middle_end/closure/closure.ml @@ -183,10 +183,10 @@ let prim_size prim args = | Pbytesrefs | Pbytessets -> 6 | Pmakearray _ -> 5 + List.length args | Parraylength kind -> if kind = Pgenarray then 6 else 2 - | Parrayrefu kind -> if kind = Pgenarray then 12 else 2 - | Parraysetu kind -> if kind = Pgenarray then 16 else 4 - | Parrayrefs kind -> if kind = Pgenarray then 18 else 8 - | Parraysets kind -> if kind = Pgenarray then 22 else 10 + | Parrayrefu kind -> (match kind with Pgenarray_ref _ -> 12 | _ -> 2) + | Parraysetu kind -> (match kind with Pgenarray_set _ -> 16 | _ -> 4) + | Parrayrefs kind -> (match kind with Pgenarray_ref _ -> 18 | _ -> 8) + | Parraysets kind -> (match kind with Pgenarray_set _ -> 22 | _ -> 10) | Pbigarrayref(_, ndims, _, _) -> 4 + ndims * 6 | Pbigarrayset(_, ndims, _, _) -> 4 + ndims * 6 | Pprobe_is_enabled _ -> 4 (* Pgetglobal and a comparison *) diff --git a/ocaml/middle_end/convert_primitives.ml b/ocaml/middle_end/convert_primitives.ml index 541af7ecf86..7cc2e8a324c 100644 --- a/ocaml/middle_end/convert_primitives.ml +++ b/ocaml/middle_end/convert_primitives.ml @@ -81,10 +81,10 @@ let convert (prim : Lambda.primitive) : Clambda_primitives.primitive = | Pmakearray (kind, mutability, mode) -> Pmakearray (kind, mutability, mode) | Pduparray (kind, mutability) -> Pduparray (kind, mutability) | Parraylength kind -> Parraylength kind - | Parrayrefu kind -> Parrayrefu kind - | Parraysetu kind -> Parraysetu kind - | Parrayrefs kind -> Parrayrefs kind - | Parraysets kind -> Parraysets kind + | Parrayrefu rkind -> Parrayrefu rkind + | Parraysetu skind -> Parraysetu skind + | Parrayrefs rkind -> Parrayrefs rkind + | Parraysets skind -> Parraysets skind | Pisint _ -> Pisint | Pisout -> Pisout | Pcvtbint (src, dest, m) -> Pcvtbint (src, dest, m) diff --git a/ocaml/middle_end/flambda/inline_and_simplify.ml b/ocaml/middle_end/flambda/inline_and_simplify.ml index fde0b45f8a1..b33318cefbe 100644 --- a/ocaml/middle_end/flambda/inline_and_simplify.ml +++ b/ocaml/middle_end/flambda/inline_and_simplify.ml @@ -1102,34 +1102,34 @@ and simplify_named env r (tree : Flambda.named) : Flambda.named * R.t = end end | Pfield _, _, _ -> Misc.fatal_error "Pfield arity error" - | (Parraysetu kind | Parraysets kind), + | (Parraysetu skind | Parraysets skind), [_block; _field; _value], [block_approx; _field_approx; value_approx] -> if A.warn_on_mutation block_approx then begin Location.prerr_warning (Debuginfo.to_location dbg) Warnings.Flambda_assignment_to_non_mutable_value end; - let kind = + let skind = let check () = - match kind with - | Pfloatarray | Pgenarray -> () - | Paddrarray | Pintarray -> + match skind with + | Pfloatarray_set | Pgenarray_set _ -> () + | Paddrarray_set _ | Pintarray_set -> (* CR pchambart: Do a proper warning here *) Misc.fatal_errorf "Assignment of a float to a specialised \ non-float array: %a" Flambda.print_named tree in match A.descr block_approx, A.descr value_approx with - | (Value_float_array _, _) -> check (); Lambda.Pfloatarray + | (Value_float_array _, _) -> check (); Lambda.Pfloatarray_set | (_, Value_float _) when Config.flat_float_array -> - check (); Lambda.Pfloatarray + check (); Lambda.Pfloatarray_set (* CR pchambart: This should be accounted by the benefit *) | _ -> - kind + skind in let prim : Clambda_primitives.primitive = match prim with - | Parraysetu _ -> Parraysetu kind - | Parraysets _ -> Parraysets kind + | Parraysetu _ -> Parraysetu skind + | Parraysets _ -> Parraysets skind | _ -> assert false in Prim (prim, args, dbg), ret r (A.value_unknown Other) diff --git a/ocaml/middle_end/flambda/inlining_cost.ml b/ocaml/middle_end/flambda/inlining_cost.ml index fdfe65b2284..7957aa41a0c 100644 --- a/ocaml/middle_end/flambda/inlining_cost.ml +++ b/ocaml/middle_end/flambda/inlining_cost.ml @@ -44,13 +44,13 @@ let prim_size (prim : Clambda_primitives.primitive) args = | Pmakearray _ -> 5 + List.length args | Parraylength Pgenarray -> 6 | Parraylength _ -> 2 - | Parrayrefu Pgenarray -> 12 + | Parrayrefu (Pgenarray_ref _) -> 12 | Parrayrefu _ -> 2 - | Parraysetu Pgenarray -> 16 + | Parraysetu (Pgenarray_set _) -> 16 | Parraysetu _ -> 4 - | Parrayrefs Pgenarray -> 18 + | Parrayrefs (Pgenarray_ref _) -> 18 | Parrayrefs _ -> 8 - | Parraysets Pgenarray -> 22 + | Parraysets (Pgenarray_set _) -> 22 | Parraysets _ -> 10 | Pbigarrayref (_, ndims, _, _) -> 4 + ndims * 6 | Pbigarrayset (_, ndims, _, _) -> 4 + ndims * 6 diff --git a/ocaml/middle_end/printclambda_primitives.ml b/ocaml/middle_end/printclambda_primitives.ml index f8762b18c4f..b7e41451b17 100644 --- a/ocaml/middle_end/printclambda_primitives.ml +++ b/ocaml/middle_end/printclambda_primitives.ml @@ -41,6 +41,30 @@ let array_kind array_kind = | Pintarray -> "int" | Pfloatarray -> "float" +let pp_array_ref_kind ppf k = + let open Lambda in + let pp_mode ppf = function + | Alloc_heap -> () + | Alloc_local -> fprintf ppf "(local)" + in + match k with + | Pgenarray_ref mode -> fprintf ppf "gen%a" pp_mode mode + | Paddrarray_ref -> fprintf ppf "addr" + | Pintarray_ref -> fprintf ppf "int" + | Pfloatarray_ref mode -> fprintf ppf "float%a" pp_mode mode + +let pp_array_set_kind ppf k = + let open Lambda in + let pp_mode ppf = function + | Modify_heap -> () + | Modify_maybe_stack -> fprintf ppf "(local)" + in + match k with + | Pgenarray_set mode -> fprintf ppf "gen%a" pp_mode mode + | Paddrarray_set mode -> fprintf ppf "addr%a" pp_mode mode + | Pintarray_set -> fprintf ppf "int" + | Pfloatarray_set -> fprintf ppf "float" + let access_size size = let open Clambda_primitives in match size with @@ -171,10 +195,10 @@ let primitive ppf (prim:Clambda_primitives.primitive) = | Pduparray (k, Immutable) -> fprintf ppf "duparray_imm[%s]" (array_kind k) | Pduparray (k, Immutable_unique) -> fprintf ppf "duparray_unique[%s]" (array_kind k) - | Parrayrefu k -> fprintf ppf "array.unsafe_get[%s]" (array_kind k) - | Parraysetu k -> fprintf ppf "array.unsafe_set[%s]" (array_kind k) - | Parrayrefs k -> fprintf ppf "array.get[%s]" (array_kind k) - | Parraysets k -> fprintf ppf "array.set[%s]" (array_kind k) + | Parrayrefu rk -> fprintf ppf "array.unsafe_get[%a]" pp_array_ref_kind rk + | Parraysetu sk -> fprintf ppf "array.unsafe_set[%a]" pp_array_set_kind sk + | Parrayrefs rk -> fprintf ppf "array.get[%a]" pp_array_ref_kind rk + | Parraysets sk -> fprintf ppf "array.set[%a]" pp_array_set_kind sk | Pisint -> fprintf ppf "isint" | Pisout -> fprintf ppf "isout" | Pbintofint (bi,m) -> print_boxed_integer "of_int" ppf bi m diff --git a/ocaml/middle_end/semantics_of_primitives.ml b/ocaml/middle_end/semantics_of_primitives.ml index c1763e7849b..b54819da50d 100644 --- a/ocaml/middle_end/semantics_of_primitives.ml +++ b/ocaml/middle_end/semantics_of_primitives.ml @@ -159,8 +159,8 @@ let return_type_of_primitive (prim:Clambda_primitives.primitive) = | Pmulfloat _ | Pdivfloat _ | Pfloatfield _ - | Parrayrefu Pfloatarray - | Parrayrefs Pfloatarray -> + | Parrayrefu (Pfloatarray_ref _) + | Parrayrefs (Pfloatarray_ref _) -> Float | _ -> Other diff --git a/ocaml/runtime/array.c b/ocaml/runtime/array.c index a011ebdb2a3..40e65f957b8 100644 --- a/ocaml/runtime/array.c +++ b/ocaml/runtime/array.c @@ -77,6 +77,22 @@ CAMLprim value caml_floatarray_get(value array, value index) return res; } +/* [ floatarray -> int -> local_ float ] */ +CAMLprim value caml_floatarray_get_local(value array, value index) +{ + intnat idx = Long_val(index); + double d; + value res; + + CAMLassert (Tag_val(array) == Double_array_tag); + if (idx < 0 || idx >= Wosize_val(array) / Double_wosize) + caml_array_bound_error(); + d = Double_flat_field(array, idx); + res = caml_alloc_local(Double_wosize, Double_tag); + Store_double_val(res, d); + return res; +} + /* [ 'a array -> int -> 'a ] */ CAMLprim value caml_array_get(value array, value index) { @@ -89,6 +105,18 @@ CAMLprim value caml_array_get(value array, value index) return caml_array_get_addr(array, index); } +/* [ local_ 'a array -> int -> local_ 'a ] */ +CAMLprim value caml_array_get_local(value array, value index) +{ +#ifdef FLAT_FLOAT_ARRAY + if (Tag_val(array) == Double_array_tag) + return caml_floatarray_get_local(array, index); +#else + CAMLassert (Tag_val(array) != Double_array_tag); +#endif + return caml_array_get_addr(array, index); +} + /* [ 'a array -> int -> 'a -> unit ] where 'a != float */ CAMLprim value caml_array_set_addr(value array, value index, value newval) { @@ -98,7 +126,20 @@ CAMLprim value caml_array_set_addr(value array, value index, value newval) return Val_unit; } -/* [ floatarray -> int -> float -> unit ] */ +/* [ local_ 'a array -> int -> local_ 'a -> unit ] where 'a != float + + Must be used carefully, as it can violate the "no forward pointers" + restriction on the local stack. */ +CAMLprim value caml_array_set_addr_local(value array, value index, value newval) +{ + intnat idx = Long_val(index); + if (idx < 0 || idx >= Wosize_val(array)) caml_array_bound_error(); + caml_modify_local(array, idx, newval); + return Val_unit; +} + +/* [ floatarray -> int -> float -> unit ] + [ local_ floatarray -> int -> local_ float -> unit ] */ CAMLprim value caml_floatarray_set(value array, value index, value newval) { intnat idx = Long_val(index); @@ -122,6 +163,22 @@ CAMLprim value caml_array_set(value array, value index, value newval) return caml_array_set_addr(array, index, newval); } +/* [ local_ 'a array -> int -> local_ 'a -> unit ] + + Must be used carefully, as it can violate the "no forward pointers" + restriction on the local stack if the array contains pointers (vs. [int]s or + unboxed floats). */ +CAMLprim value caml_array_set_local(value array, value index, value newval) +{ +#ifdef FLAT_FLOAT_ARRAY + if (Tag_val(array) == Double_array_tag) + return caml_floatarray_set(array, index, newval); +#else + CAMLassert (Tag_val(array) != Double_array_tag); +#endif + return caml_array_set_addr_local(array, index, newval); +} + /* [ floatarray -> int -> float ] */ CAMLprim value caml_floatarray_unsafe_get(value array, value index) { @@ -140,6 +197,20 @@ CAMLprim value caml_floatarray_unsafe_get(value array, value index) return res; } +/* [ floatarray -> int -> local_ float ] */ +CAMLprim value caml_floatarray_unsafe_get_local(value array, value index) +{ + intnat idx = Long_val(index); + double d; + value res; + + CAMLassert (Tag_val(array) == Double_array_tag); + d = Double_flat_field(array, idx); + res = caml_alloc_local(Double_wosize, Double_tag); + Store_double_val(res, d); + return res; +} + /* [ 'a array -> int -> 'a ] */ CAMLprim value caml_array_unsafe_get(value array, value index) { @@ -152,6 +223,18 @@ CAMLprim value caml_array_unsafe_get(value array, value index) return Field(array, Long_val(index)); } +/* [ local_ 'a array -> int -> local_ 'a ] */ +CAMLprim value caml_array_unsafe_get_local(value array, value index) +{ +#ifdef FLAT_FLOAT_ARRAY + if (Tag_val(array) == Double_array_tag) + return caml_floatarray_unsafe_get_local(array, index); +#else + CAMLassert (Tag_val(array) != Double_array_tag); +#endif + return Field(array, Long_val(index)); +} + /* [ 'a array -> int -> 'a -> unit ] where 'a != float */ static value caml_array_unsafe_set_addr(value array, value index,value newval) { @@ -160,7 +243,20 @@ static value caml_array_unsafe_set_addr(value array, value index,value newval) return Val_unit; } -/* [ floatarray -> int -> float -> unit ] */ +/* [ local_ 'a array -> int -> local_ 'a -> unit ] where 'a != float + + Must be used carefully, as it can violate the "no forward pointers" + restriction on the local stack. */ +static value caml_array_unsafe_set_addr_local(value array, value index, + value newval) +{ + intnat idx = Long_val(index); + caml_modify_local(array, idx, newval); + return Val_unit; +} + +/* [ floatarray -> int -> float -> unit ] + [ local_ floatarray -> int -> local_ float -> unit ] */ CAMLprim value caml_floatarray_unsafe_set(value array, value index,value newval) { intnat idx = Long_val(index); @@ -181,6 +277,23 @@ CAMLprim value caml_array_unsafe_set(value array, value index, value newval) return caml_array_unsafe_set_addr(array, index, newval); } +/* [ local_ 'a array -> int -> local_ 'a -> unit ] + + Must be used carefully, as it can violate the "no forward pointers" + restriction on the local stack if the array contains pointers (vs. [int]s or + unboxed floats). */ +CAMLprim value caml_array_unsafe_set_local(value array, value index, + value newval) +{ +#ifdef FLAT_FLOAT_ARRAY + if (Tag_val(array) == Double_array_tag) + return caml_floatarray_unsafe_set(array, index, newval); +#else + CAMLassert (Tag_val(array) != Double_array_tag); +#endif + return caml_array_unsafe_set_addr_local(array, index, newval); +} + /* [len] is a [value] representing number of floats. */ /* [ int -> floatarray ] */ CAMLprim value caml_floatarray_create(value len) @@ -621,3 +734,13 @@ CAMLprim value caml_array_fill(value array, } return Val_unit; } + +CAMLprim value caml_iarray_of_array(value a) +{ + return a; +} + +CAMLprim value caml_array_of_iarray(value a) +{ + return a; +} diff --git a/ocaml/runtime/str.c b/ocaml/runtime/str.c index 4bfac095474..aa5b650ccf3 100644 --- a/ocaml/runtime/str.c +++ b/ocaml/runtime/str.c @@ -480,13 +480,3 @@ CAMLprim value caml_bytes_of_string(value bv) { return bv; } - -CAMLprim value caml_iarray_of_array(value bv) -{ - return bv; -} - -CAMLprim value caml_array_of_iarray(value bv) -{ - return bv; -} diff --git a/ocaml/stdlib/iarray.ml b/ocaml/stdlib/iarray.ml index af0a7dfa80c..82d6299e7c4 100644 --- a/ocaml/stdlib/iarray.ml +++ b/ocaml/stdlib/iarray.ml @@ -17,6 +17,10 @@ open! Stdlib +(* NOTE: If you update this file, please also update iarrayLabels.ml; from the + declaration of [type +'a t = 'a iarray] on down, they're the same. This is a + temporary state of affairs, but for now, please copy things! *) + (* In this file, we use four different implementation strategies: 1. Reusing [external]s for mutable arrays. (E.g., [get].) @@ -51,93 +55,330 @@ open! Stdlib [@@@ocaml.flambda_o3] -(* If you update this file please also update iarrayLabels.ml *) - (* An alias for the type of immutable arrays. *) type +'a t = 'a iarray (* Array operations *) -external length : 'a iarray -> int = "%array_length" -external get : 'a iarray -> int -> 'a = "%array_safe_get" -external ( .:() ) : 'a iarray -> int -> 'a = "%array_safe_get" -external unsafe_get : 'a iarray -> int -> 'a = "%array_unsafe_get" +external length : local_ 'a iarray -> int = "%array_length" +external get : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_safe_get" +external ( .:() ) : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_safe_get" +external unsafe_get : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_unsafe_get" external concat : 'a iarray list -> 'a iarray = "caml_array_concat" +external concat_local : local_ 'a iarray list -> local_ 'a iarray = + "caml_array_concat_local" external append_prim : 'a iarray -> 'a iarray -> 'a iarray = "caml_array_append" +external append_prim_local : + local_ 'a iarray -> local_ 'a iarray -> local_ 'a iarray = + "caml_array_append_local" external unsafe_sub : 'a iarray -> int -> int -> 'a iarray = "caml_array_sub" +external unsafe_sub_local : local_ 'a iarray -> int -> int -> local_ 'a iarray = + "caml_array_sub_local" external unsafe_of_array : 'a array -> 'a iarray = "%array_to_iarray" external unsafe_to_array : 'a iarray -> 'a array = "%array_of_iarray" -let init l f = unsafe_of_array (Array.init l f) +(* Used only to reimplement [init] *) +external unsafe_set_mutable : 'a array -> int -> 'a -> unit = + "%array_unsafe_set" + +(* VERY UNSAFE: Any of these functions can be used to violate the "no forward + pointers" restriction for the local stack if not used carefully. Each of + these can either make a local mutable array or mutate its contents, and if + not careful, this can lead to an array's contents pointing forwards. The + latter two functions could be overloaded via [[@local_opt]], but we don't do + that in order to isolate the unsafety. *) +external make_mutable_local : int -> local_ 'a -> local_ 'a array = + "caml_make_local_vect" +external unsafe_of_local_array : local_ 'a array -> local_ 'a iarray = + "%array_to_iarray" +external unsafe_set_local : local_ 'a array -> int -> local_ 'a -> unit = + "%array_unsafe_set" + +(* We can't use immutable array literals in this file, since we don't want to + require the stdlib to be compiled with extensions, so instead of [[::]] we + use [unsafe_of_(local_)array [||]] below. *) + +(* Really trusting the inliner here; to get maximum performance, it has to + inline both [unsafe_init_local] *and* [f]. *) +(** Precondition: [l >= 0]. *) +let[@inline always] unsafe_init_local l (local_ f : int -> local_ 'a) = local_ + if l = 0 then + unsafe_of_local_array [||] + else + (* The design of this function is exceedingly delicate, and is the only way + we can correctly allocate a local array on the stack via mutation. We + are subject to the "no forward pointers" constraint on the local stack; + we're not allowed to make pointers to later-allocated objects even within + the same stack frame. Thus, in order to get this right, we consume O(n) + call-stack space: we allocate the values to put in the array, and only + *then* recurse, creating the array as the very last thing of all and + *returning* it. This is why the [f i] call is the first thing in the + function, and why it's not tail-recursive; if it were tail-recursive, + then we wouldn't have anywhere to put the array elements during the whole + process. *) + let rec go i = local_ begin + let x = f i in + if i = l - 1 then + make_mutable_local l x + else begin + let res = go (i+1) in + unsafe_set_local res i x; + res + end + end in + unsafe_of_local_array (go 0) + +(* The implementation is copied from [Array] so that [f] can be [local_] *) +let init l (local_ f) = + if l = 0 then unsafe_of_array [||] else + if l < 0 then invalid_arg "Iarray.init" + (* See #6575. We could also check for maximum array size, but this depends + on whether we create a float array or a regular one... *) + else + let res = Array.make l (f 0) in + for i = 1 to pred l do + unsafe_set_mutable res i (f i) + done; + unsafe_of_array res + +let init_local l f = local_ + if l < 0 then invalid_arg "Iarray.init_local" + (* See #6575. We could also check for maximum array size, but this depends + on whether we create a float array or a regular one... *) + else unsafe_init_local l f let append a1 a2 = if length a1 = 0 then a2 (* Safe because they're immutable *) else if length a2 = 0 then a1 else append_prim a1 a2 +let append_local a1 a2 = local_ + if length a1 = 0 then a2 (* Safe because they're immutable *) + else if length a2 = 0 then a1 + else append_prim_local a1 a2 + let sub a ofs len = if ofs < 0 || len < 0 || ofs > length a - len then invalid_arg "Iarray.sub" else unsafe_sub a ofs len +let sub_local a ofs len = local_ + if ofs < 0 || len < 0 || ofs > length a - len + then invalid_arg "Iarray.sub" + else unsafe_sub_local a ofs len + let iter f a = for i = 0 to length a - 1 do f(unsafe_get a i) done +let iter_local f a = + for i = 0 to length a - 1 do f(unsafe_get a i) done + let iter2 f a b = if length a <> length b then invalid_arg "Iarray.iter2: arrays must have the same length" else for i = 0 to length a - 1 do f (unsafe_get a i) (unsafe_get b i) done +let iter2_local f a b = + if length a <> length b then + invalid_arg "Iarray.iter2_local: arrays must have the same length" + else + for i = 0 to length a - 1 do f (unsafe_get a i) (unsafe_get b i) done + +let iter2_local_first f a b = + if length a <> length b then + invalid_arg "Iarray.iter2_local_first: arrays must have the same length" + else + for i = 0 to length a - 1 do f (unsafe_get a i) (unsafe_get b i) done + +let iter2_local_second f a b = + if length a <> length b then + invalid_arg "Iarray.iter2_local_second: arrays must have the same length" + else + for i = 0 to length a - 1 do f (unsafe_get a i) (unsafe_get b i) done + let map f a = let l = length a in - let r = if l = 0 then [||] else begin + if l = 0 then unsafe_of_array [||] else begin let r = Array.make l (f(unsafe_get a 0)) in for i = 1 to l - 1 do Array.unsafe_set r i (f(unsafe_get a i)) done; - r - end in - unsafe_of_array r + unsafe_of_array r + end + +let map_local f a = local_ + unsafe_init_local (length a) (fun i -> local_ f (unsafe_get a i)) + +let map_local_input f a = + let l = length a in + if l = 0 then unsafe_of_array [||] else begin + let r = Array.make l (f(unsafe_get a 0)) in + for i = 1 to l - 1 do + Array.unsafe_set r i (f(unsafe_get a i)) + done; + unsafe_of_array r + end + +let map_local_output f a = local_ + unsafe_init_local (length a) (fun i -> local_ f (unsafe_get a i)) let map2 f a b = let la = length a in let lb = length b in if la <> lb then - invalid_arg "Array.map2: arrays must have the same length" + invalid_arg "Iarray.map2: arrays must have the same length" else begin - let r = if la = 0 then [||] else begin + if la = 0 then unsafe_of_array [||] else begin let r = Array.make la (f (unsafe_get a 0) (unsafe_get b 0)) in for i = 1 to la - 1 do Array.unsafe_set r i (f (unsafe_get a i) (unsafe_get b i)) done; - r - end in - unsafe_of_array r + unsafe_of_array r + end + end + +let map2_local f a b = local_ + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2_local: arrays must have the same length" + else + unsafe_init_local la (fun i -> local_ f (unsafe_get a i) (unsafe_get b i)) + +let map2_local_inputs f a b = + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2: arrays must have the same length" + else begin + if la = 0 then unsafe_of_array [||] else begin + let r = Array.make la (f (unsafe_get a 0) (unsafe_get b 0)) in + for i = 1 to la - 1 do + Array.unsafe_set r i (f (unsafe_get a i) (unsafe_get b i)) + done; + unsafe_of_array r + end end +let map2_local_output f a b = local_ + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2_local: arrays must have the same length" + else + unsafe_init_local la (fun i -> local_ f (unsafe_get a i) (unsafe_get b i)) + +let map2_local_first_input f a b = + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2: arrays must have the same length" + else begin + if la = 0 then unsafe_of_array [||] else begin + let r = Array.make la (f (unsafe_get a 0) (unsafe_get b 0)) in + for i = 1 to la - 1 do + Array.unsafe_set r i (f (unsafe_get a i) (unsafe_get b i)) + done; + unsafe_of_array r + end + end + +let map2_local_second_input f a b = + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2: arrays must have the same length" + else begin + if la = 0 then unsafe_of_array [||] else begin + let r = Array.make la (f (unsafe_get a 0) (unsafe_get b 0)) in + for i = 1 to la - 1 do + Array.unsafe_set r i (f (unsafe_get a i) (unsafe_get b i)) + done; + unsafe_of_array r + end + end + +let map2_local_first_input_and_output f a b = local_ + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2_local: arrays must have the same length" + else + unsafe_init_local la (fun i -> local_ f (unsafe_get a i) (unsafe_get b i)) + +let map2_local_second_input_and_output f a b = local_ + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2_local: arrays must have the same length" + else + unsafe_init_local la (fun i -> local_ f (unsafe_get a i) (unsafe_get b i)) + let iteri f a = for i = 0 to length a - 1 do f i (unsafe_get a i) done +let iteri_local f a = + for i = 0 to length a - 1 do f i (unsafe_get a i) done + let mapi f a = let l = length a in - let r = if l = 0 then [||] else begin + if l = 0 then unsafe_of_array [||] else begin let r = Array.make l (f 0 (unsafe_get a 0)) in for i = 1 to l - 1 do Array.unsafe_set r i (f i (unsafe_get a i)) done; - r - end in - unsafe_of_array r + unsafe_of_array r + end + +let mapi_local f a = local_ + unsafe_init_local (length a) (fun i -> local_ f i (unsafe_get a i)) + +let mapi_local_input f a = + let l = length a in + if l = 0 then unsafe_of_array [||] else begin + let r = Array.make l (f 0 (unsafe_get a 0)) in + for i = 1 to l - 1 do + Array.unsafe_set r i (f i (unsafe_get a i)) + done; + unsafe_of_array r + end + +let mapi_local_output f a = local_ + unsafe_init_local (length a) (fun i -> local_ f i (unsafe_get a i)) let to_list a = let rec tolist i res = if i < 0 then res else tolist (i - 1) (unsafe_get a i :: res) in tolist (length a - 1) [] +let to_list_local a = local_ + let rec tolist i res = local_ + if i < 0 then res else tolist (i - 1) (unsafe_get a i :: res) in + tolist (length a - 1) [] + let of_list l = unsafe_of_array (Array.of_list l) +(* Cannot use List.length here because the List module depends on Array. *) +let rec list_length accu = function + | [] -> accu + | _::t -> list_length (succ accu) t + +(* This shouldn't violate the forward-pointers restriction because the list + elements already exist *) +let of_list_local = function + | [] -> local_ unsafe_of_array [||] + | hd::tl as l -> local_ + let a = make_mutable_local (list_length 0 l) hd in + let rec fill i = function + | [] -> local_ a + | hd::tl -> local_ unsafe_set_local a i hd; fill (i+1) tl in + unsafe_of_local_array (fill 1 tl) + let to_array ia = Array.copy (unsafe_to_array ia) let of_array ma = unsafe_of_array (Array.copy ma) @@ -149,9 +390,34 @@ let fold_left f x a = done; !r +let fold_left_local f x a = local_ + let len = length a in + let rec go r i = local_ + if i = len + then r + else go (f r (unsafe_get a i)) (i+1) + in + go x 0 + +let fold_left_local_input f x a = + let r = ref x in + for i = 0 to length a - 1 do + r := f !r (unsafe_get a i) + done; + !r + +let fold_left_local_output f x a = local_ + let len = length a in + let rec go r i = local_ + if i = len + then r + else go (f r (unsafe_get a i)) (i+1) + in + go x 0 + let fold_left_map f acc input_array = let len = length input_array in - let acc, output_array = if len = 0 then (acc, [||]) else begin + if len = 0 then (acc, unsafe_of_array [||]) else begin let acc, elt = f acc (unsafe_get input_array 0) in let output_array = Array.make len elt in let acc = ref acc in @@ -160,9 +426,56 @@ let fold_left_map f acc input_array = acc := acc'; Array.unsafe_set output_array i elt; done; - !acc, output_array - end in - acc, unsafe_of_array output_array + !acc, unsafe_of_array output_array + end + +let fold_left_map_local f acc input_array = local_ + let len = length input_array in + if len = 0 then (acc, unsafe_of_local_array [||]) else begin + let rec go acc i = local_ + let acc', elt = f acc (unsafe_get input_array i) in + if i = len - 1 then + acc', make_mutable_local len elt + else begin + let (_, output_array) as res = go acc (i+1) in + unsafe_set_local output_array i elt; + res + end + in + let acc, output_array = go acc 0 in + acc, unsafe_of_local_array output_array + end + +let fold_left_map_local_input f acc input_array = + let len = length input_array in + if len = 0 then (acc, unsafe_of_array [||]) else begin + let acc, elt = f acc (unsafe_get input_array 0) in + let output_array = Array.make len elt in + let acc = ref acc in + for i = 1 to len - 1 do + let acc', elt = f !acc (unsafe_get input_array i) in + acc := acc'; + Array.unsafe_set output_array i elt; + done; + !acc, unsafe_of_array output_array + end + +let fold_left_map_local_output f acc input_array = local_ + let len = length input_array in + if len = 0 then (acc, unsafe_of_local_array [||]) else begin + let rec go acc i = local_ + let acc', elt = f acc (unsafe_get input_array i) in + if i = len - 1 then + acc', make_mutable_local len elt + else begin + let (_, output_array) as res = go acc (i+1) in + unsafe_set_local output_array i elt; + res + end + in + let acc, output_array = go acc 0 in + acc, unsafe_of_local_array output_array + end let fold_right f a x = let r = ref x in @@ -171,57 +484,160 @@ let fold_right f a x = done; !r +let fold_right_local f a x = local_ + let rec go r i = local_ + if i = -1 + then r + else go (f (unsafe_get a i) r) (i-1) + in + go x (length a - 1) + +let fold_right_local_input f a x = + let r = ref x in + for i = length a - 1 downto 0 do + r := f (unsafe_get a i) !r + done; + !r + +let fold_right_local_output f a x = local_ + let rec go r i = local_ + if i = -1 + then r + else go (f (unsafe_get a i) r) (i-1) + in + go x (length a - 1) + +(* CR aspectorzabusky: Why do I need this? Shouldn't mode-crossing handle doing + this? *) +let[@inline always] globalize_bool : local_ bool -> bool = fun b -> b + let exists p a = let n = length a in - let rec loop i = + let rec loop i = local_ if i = n then false else if p (unsafe_get a i) then true else loop (succ i) in - loop 0 + globalize_bool (loop 0) + +let exists_local p a = + let n = length a in + let rec loop i = local_ + if i = n then false + else if p (unsafe_get a i) then true + else loop (succ i) in + globalize_bool (loop 0) let for_all p a = let n = length a in - let rec loop i = + let rec loop i = local_ if i = n then true else if p (unsafe_get a i) then loop (succ i) else false in - loop 0 + globalize_bool (loop 0) + +let for_all_local p a = + let n = length a in + let rec loop i = local_ + if i = n then true + else if p (unsafe_get a i) then loop (succ i) + else false in + globalize_bool (loop 0) let for_all2 p l1 l2 = let n1 = length l1 and n2 = length l2 in if n1 <> n2 then invalid_arg "Iarray.for_all2" - else let rec loop i = + else let rec loop i = local_ if i = n1 then true else if p (unsafe_get l1 i) (unsafe_get l2 i) then loop (succ i) else false in - loop 0 + globalize_bool (loop 0) + +let for_all2_local p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.for_all2_local" + else let rec loop i = local_ + if i = n1 then true + else if p (unsafe_get l1 i) (unsafe_get l2 i) then loop (succ i) + else false in + globalize_bool (loop 0) + +let for_all2_local_first p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.for_all2_local_first" + else let rec loop i = local_ + if i = n1 then true + else if p (unsafe_get l1 i) (unsafe_get l2 i) then loop (succ i) + else false in + globalize_bool (loop 0) + +let for_all2_local_second p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.for_all2_local_second" + else let rec loop i = local_ + if i = n1 then true + else if p (unsafe_get l1 i) (unsafe_get l2 i) then loop (succ i) + else false in + globalize_bool (loop 0) let exists2 p l1 l2 = let n1 = length l1 and n2 = length l2 in if n1 <> n2 then invalid_arg "Iarray.exists2" - else let rec loop i = + else let rec loop i = local_ if i = n1 then false else if p (unsafe_get l1 i) (unsafe_get l2 i) then true else loop (succ i) in - loop 0 + globalize_bool (loop 0) + +let exists2_local p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.exists2_local" + else let rec loop i = local_ + if i = n1 then false + else if p (unsafe_get l1 i) (unsafe_get l2 i) then true + else loop (succ i) in + globalize_bool (loop 0) + +let exists2_local_first p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.exists2_local_first" + else let rec loop i = local_ + if i = n1 then false + else if p (unsafe_get l1 i) (unsafe_get l2 i) then true + else loop (succ i) in + globalize_bool (loop 0) + +let exists2_local_second p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.exists2_local_second" + else let rec loop i = local_ + if i = n1 then false + else if p (unsafe_get l1 i) (unsafe_get l2 i) then true + else loop (succ i) in + globalize_bool (loop 0) let mem x a = let n = length a in - let rec loop i = + let rec loop i = local_ if i = n then false else if compare (unsafe_get a i) x = 0 then true else loop (succ i) in - loop 0 + globalize_bool (loop 0) let memq x a = let n = length a in - let rec loop i = + let rec loop i = local_ if i = n then false else if x == (unsafe_get a i) then true else loop (succ i) in - loop 0 + globalize_bool (loop 0) let find_opt p a = let n = length a in @@ -232,6 +648,17 @@ let find_opt p a = if p x then Some x else loop (succ i) in + loop 0 [@nontail] + +let find_opt_local p a = local_ + let n = length a in + let rec loop i = local_ + if i = n then None + else + let x = unsafe_get a i in + if p x then Some x + else loop (succ i) + in loop 0 let find_map f a = @@ -243,12 +670,44 @@ let find_map f a = | None -> loop (succ i) | Some _ as r -> r in + loop 0 [@nontail] + +let find_map_local f a = local_ + let n = length a in + let rec loop i = local_ + if i = n then None + else + match f (unsafe_get a i) with + | None -> loop (succ i) + | Some _ as r -> r + in + loop 0 + +let find_map_local_input f a = + let n = length a in + let rec loop i = + if i = n then None + else + match f (unsafe_get a i) with + | None -> loop (succ i) + | Some _ as r -> r + in + loop 0 [@nontail] + +let find_map_local_output f a = local_ + let n = length a in + let rec loop i = local_ + if i = n then None + else + match f (unsafe_get a i) with + | None -> loop (succ i) + | Some _ as r -> r + in loop 0 let split x = - (* We can't use immutable array literals here, since we don't want to require - the stdlib to be compiled with extensions *) - let r1, r2 = if x = unsafe_of_array [||] then [||], [||] + if x = unsafe_of_array [||] + then unsafe_of_array [||], unsafe_of_array [||] else begin let a0, b0 = unsafe_get x 0 in let n = length x in @@ -259,14 +718,32 @@ let split x = Array.unsafe_set a i ai; Array.unsafe_set b i bi done; - a, b - end in - unsafe_of_array r1, unsafe_of_array r2 + unsafe_of_array a, unsafe_of_array b + end + +(* This shouldn't violate the forward-pointers restriction because the array + elements already exist. (This doesn't work for [combine], where we need to + create the tuples.) *) +let split_local x = local_ + if x = unsafe_of_array [||] + then unsafe_of_array [||], unsafe_of_array [||] + else begin + let a0, b0 = unsafe_get x 0 in + let n = length x in + let a = make_mutable_local n a0 in + let b = make_mutable_local n b0 in + for i = 1 to n - 1 do + let ai, bi = unsafe_get x i in + unsafe_set_local a i ai; + unsafe_set_local b i bi + done; + unsafe_of_local_array a, unsafe_of_local_array b + end let combine a b = let na = length a in let nb = length b in - if na <> nb then invalid_arg "Array.combine"; + if na <> nb then invalid_arg "Iarray.combine"; let r = if na = 0 then [||] else begin let x = Array.make na (unsafe_get a 0, unsafe_get b 0) in @@ -277,6 +754,12 @@ let combine a b = end in unsafe_of_array r +let combine_local a b = local_ + let na = length a in + let nb = length b in + if na <> nb then invalid_arg "Iarray.combine_local"; + unsafe_init_local na (fun i -> local_ unsafe_get a i, unsafe_get b i) + (* Must be fully applied due to the value restriction *) let lift_sort sorter cmp iarr = let arr = to_array iarr in diff --git a/ocaml/stdlib/iarray.mli b/ocaml/stdlib/iarray.mli index ff2b91b9f80..48eeecde784 100644 --- a/ocaml/stdlib/iarray.mli +++ b/ocaml/stdlib/iarray.mli @@ -26,17 +26,20 @@ open! Stdlib *) (** Operations on immutable arrays. This module mirrors the API of [Array], but - omits functions that assume mutability; in particular, it omits [copy] along - with the functions [make], [create_float], and [make_matrix] that produce - all-constant arrays. *) + omits functions that assume mutability; in addition to obviously mutating + functions, it omits [copy] along with the functions [make], [create_float], + and [make_matrix] that produce all-constant arrays. The exception is the + sorting functions, which are given a copying API to replace the in-place + one. *) type +'a t = 'a iarray (** An alias for the type of immutable arrays. *) -external length : 'a iarray -> int = "%array_length" +external length : local_ 'a iarray -> int = "%array_length" (** Return the length (number of elements) of the given immutable array. *) -external get : 'a iarray -> int -> 'a = "%array_safe_get" +external get : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_safe_get" (** [get a n] returns the element number [n] of immutable array [a]. The first element has number 0. The last element has number [length a - 1]. @@ -45,10 +48,11 @@ external get : 'a iarray -> int -> 'a = "%array_safe_get" @raise Invalid_argument if [n] is outside the range 0 to [(length a - 1)]. *) -external ( .:() ) : 'a iarray -> int -> 'a = "%array_safe_get" +external ( .:() ) : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_safe_get" (** A synonym for [get]. *) -val init : int -> (int -> 'a) -> 'a iarray +val init : int -> local_ (int -> 'a) -> 'a iarray (** [init n f] returns a fresh immutable array of length [n], with element number [i] initialized to the result of [f i]. In other terms, [init n f] tabulates the results of [f] @@ -58,27 +62,43 @@ val init : int -> (int -> 'a) -> 'a iarray If the return type of [f] is [float], then the maximum size is only [Sys.max_array_length / 2]. *) +val init_local : int -> local_ (int -> local_ 'a) -> local_ 'a iarray +(** The locally-allocating version of [init]. *) + val append : 'a iarray -> 'a iarray -> 'a iarray (** [append v1 v2] returns a fresh immutable array containing the concatenation of the immutable arrays [v1] and [v2]. @raise Invalid_argument if [length v1 + length v2 > Sys.max_array_length]. *) +val append_local : local_ 'a iarray -> local_ 'a iarray -> local_ 'a iarray +(** The locally-allocating version of [append]. *) + val concat : 'a iarray list -> 'a iarray (** Same as {!append}, but concatenates a list of immutable arrays. *) +val concat_local : local_ 'a iarray list -> local_ 'a iarray +(** The locally-allocating version of [concat]. *) + val sub : 'a iarray -> int -> int -> 'a iarray (** [sub a pos len] returns a fresh immutable array of length [len], containing the elements number [pos] to [pos + len - 1] - of immutable array [a]. + of immutable array [a]. This creates a copy of the selected + portion of the immutable array. @raise Invalid_argument if [pos] and [len] do not designate a valid subarray of [a]; that is, if [pos < 0], or [len < 0], or [pos + len > length a]. *) +val sub_local : local_ 'a iarray -> int -> int -> local_ 'a iarray +(** The locally-allocating version of [sub]. *) + val to_list : 'a iarray -> 'a list (** [to_list a] returns the list of all the elements of [a]. *) +val to_list_local : local_ 'a iarray -> local_ 'a list +(** The locally-allocating version of []. *) + val of_list : 'a list -> 'a iarray (** [of_list l] returns a fresh immutable array containing the elements of [l]. @@ -86,6 +106,9 @@ val of_list : 'a list -> 'a iarray @raise Invalid_argument if the length of [l] is greater than [Sys.max_array_length]. *) +val of_list_local : local_ 'a list -> local_ 'a iarray +(** The locally-allocating version of [of_list]. *) + (** {1 Converting to and from mutable arrays} *) val to_array : 'a iarray -> 'a array @@ -98,111 +121,331 @@ val of_array : 'a array -> 'a iarray (** {1 Iterators} *) -val iter : ('a -> unit) -> 'a iarray -> unit +val iter : local_ ('a -> unit) -> 'a iarray -> unit (** [iter f a] applies function [f] in turn to all the elements of [a]. It is equivalent to [f a.:(0); f a.:(1); ...; f a.:(length a - 1); ()]. *) -val iteri : (int -> 'a -> unit) -> 'a iarray -> unit +val iter_local : local_ (local_ 'a -> unit) -> local_ 'a iarray -> unit +(** The locally-scoped version of [iter]. *) + +val iteri : local_ (int -> 'a -> unit) -> 'a iarray -> unit (** Same as {!iter}, but the function is applied to the index of the element as first argument, and the element itself as second argument. *) -val map : ('a -> 'b) -> 'a iarray -> 'b iarray +val iteri_local : local_ (int -> local_ 'a -> unit) -> local_ 'a iarray -> unit +(** The locally-scoped version of [iteri]. *) + +val map : local_ ('a -> 'b) -> 'a iarray -> 'b iarray (** [map f a] applies function [f] to all the elements of [a], and builds an immutable array with the results returned by [f]: [[| f a.:(0); f a.:(1); ...; f a.:(length a - 1) |]]. *) -val mapi : (int -> 'a -> 'b) -> 'a iarray -> 'b iarray +val map_local : + local_ (local_ 'a -> local_ 'b) -> local_ 'a iarray -> local_ 'b iarray +(** The locally-scoped and locally-allocating version of [map]. *) + +val map_local_input : local_ (local_ 'a -> 'b) -> local_ 'a iarray -> 'b iarray +(** The locally-constrained but globally-allocating version of [map]. *) + +val map_local_output : local_ ('a -> local_ 'b) -> 'a iarray -> local_ 'b iarray +(** The locally-allocating but global-input version of [map]. *) + +val mapi : local_ (int -> 'a -> 'b) -> 'a iarray -> 'b iarray (** Same as {!map}, but the function is applied to the index of the element as first argument, and the element itself as second argument. *) -val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b iarray -> 'a +val mapi_local : + local_ (int -> local_ 'a -> local_ 'b) -> + local_ 'a iarray -> + local_ 'b iarray +(** The locally-scoped and locally-allocating version of [mapi]. *) + +val mapi_local_input : + local_ (int -> local_ 'a -> 'b) -> local_ 'a iarray -> 'b iarray +(** The locally-constrained but globally-allocating version of [mapi]. *) + +val mapi_local_output : + local_ (int -> 'a -> local_ 'b) -> 'a iarray -> local_ 'b iarray +(** The locally-allocating but global-input version of [mapi]. *) + +val fold_left : local_ ('a -> 'b -> 'a) -> 'a -> 'b iarray -> 'a (** [fold_left f init a] computes [f (... (f (f init a.:(0)) a.:(1)) ...) a.:(n-1)], where [n] is the length of the immutable array [a]. *) +val fold_left_local : + local_ (local_ 'a -> local_ 'b -> local_ 'a) -> + local_ 'a -> + local_ 'b iarray -> + local_ 'a +(** The locally-constrained and locally-allocating version of [fold_left]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + +val fold_left_local_input : + local_ ('a -> local_ 'b -> 'a) -> 'a -> local_ 'b iarray -> 'a +(** The locally-constrained but globally-allocating version of [fold_left]. *) + +val fold_left_local_output : + local_ (local_ 'a -> 'b -> local_ 'a) -> local_ 'a -> 'b iarray -> local_ 'a +(** The locally-allocating but global-input version of [fold_left]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + val fold_left_map : - ('a -> 'b -> 'a * 'c) -> 'a -> 'b iarray -> 'a * 'c iarray + local_ ('a -> 'b -> 'a * 'c) -> 'a -> 'b iarray -> 'a * 'c iarray (** [fold_left_map] is a combination of {!fold_left} and {!map} that threads an accumulator through calls to [f]. *) -val fold_right : ('b -> 'a -> 'a) -> 'b iarray -> 'a -> 'a +val fold_left_map_local : + local_ (local_ 'a -> local_ 'b -> local_ 'a * 'c) -> + local_ 'a -> + local_ 'b iarray -> + local_ 'a * 'c iarray +(** The locally-constrained and locally-allocating version of [fold_left]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + +val fold_left_map_local_input : + local_ ('a -> local_ 'b -> 'a * 'c) -> + 'a -> + local_ 'b iarray -> + 'a * 'c iarray +(** The locally-constrained but globally-allocating version of [fold_left]. *) + +val fold_left_map_local_output : + local_ (local_ 'a -> 'b -> local_ 'a * 'c) -> + local_ 'a -> + 'b iarray -> + local_ 'a * 'c iarray +(** The locally-allocating but global-input version of [fold_left]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + +val fold_right : local_ ('b -> 'a -> 'a) -> 'b iarray -> 'a -> 'a (** [fold_right f a init] computes [f a.:(0) (f a.:(1) ( ... (f a.:(n-1) init) ...))], where [n] is the length of the immutable array [a]. *) +val fold_right_local : + local_ (local_ 'b -> local_ 'a -> local_ 'a) -> + local_ 'b iarray -> + local_ 'a -> + local_ 'a +(** The locally-constrained and locally-allocating version of [fold_right]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + +val fold_right_local_input : + local_ (local_ 'b -> 'a -> 'a) -> local_ 'b iarray -> 'a -> 'a +(** The locally-constrained but globally-allocating version of [fold_right]. *) + +val fold_right_local_output : + local_ ('b -> local_ 'a -> local_ 'a) -> 'b iarray -> local_ 'a -> local_ 'a +(** The locally-allocating but global-input version of [fold_right]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + (** {1 Iterators on two arrays} *) -val iter2 : ('a -> 'b -> unit) -> 'a iarray -> 'b iarray -> unit +val iter2 : local_ ('a -> 'b -> unit) -> 'a iarray -> 'b iarray -> unit (** [iter2 f a b] applies function [f] to all the elements of [a] and [b]. @raise Invalid_argument if the immutable arrays are not the same size. *) -val map2 : ('a -> 'b -> 'c) -> 'a iarray -> 'b iarray -> 'c iarray +val iter2_local : + local_ (local_ 'a -> local_ 'b -> unit) -> + local_ 'a iarray -> + local_ 'b iarray -> + unit +(** The locally-scoped version of [iter2]. *) + +val iter2_local_first : + local_ (local_ 'a -> 'b -> unit) -> local_ 'a iarray -> 'b iarray -> unit +(** The first-biased partly-locally-scoped version of [iter2]. *) + +val iter2_local_second : + local_ ('a -> local_ 'b -> unit) -> 'a iarray -> local_ 'b iarray -> unit +(** The second-biased partly-locally-scoped version of [iter2]. *) + +val map2 : local_ ('a -> 'b -> 'c) -> 'a iarray -> 'b iarray -> 'c iarray (** [map2 f a b] applies function [f] to all the elements of [a] and [b], and builds an immutable array with the results returned by [f]: [[| f a.:(0) b.:(0); ...; f a.:(length a - 1) b.:(length b - 1)|]]. @raise Invalid_argument if the immutable arrays are not the same size. *) +val map2_local : + local_ (local_ 'a -> local_ 'b -> local_ 'c) -> + local_ 'a iarray -> + local_ 'b iarray -> + local_ 'c iarray +(** The locally-scoped and locally-allocating version of [map2]. *) + +val map2_local_inputs : + local_ (local_ 'a -> local_ 'b -> 'c) -> + local_ 'a iarray -> + local_ 'b iarray -> + 'c iarray +(** The locally-scoped but globally-allocating version of [map2]. *) + +val map2_local_output : + local_ ('a -> 'b -> local_ 'c) -> 'a iarray -> 'b iarray -> local_ 'c iarray +(** The locally-allocating but global-inputs version of [map2]. *) + +val map2_local_first_input : + local_ (local_ 'a -> 'b -> 'c) -> local_ 'a iarray -> 'b iarray -> 'c iarray +(** The first-biased partly-locally-scoped but globally-allocating version of + [map2]. *) + +val map2_local_second_input : + local_ ('a -> local_ 'b -> 'c) -> 'a iarray -> local_ 'b iarray -> 'c iarray +(** The second-biased partly-locally-scoped but globally-allocating version of + [map2]. *) + +val map2_local_first_input_and_output : + local_ (local_ 'a -> 'b -> local_ 'c) -> + local_ 'a iarray -> + 'b iarray -> + local_ 'c iarray +(** The locally-allocating and first-biased partly-locally-scoped version of + [map2]. *) + +val map2_local_second_input_and_output : + local_ ('a -> local_ 'b -> local_ 'c) -> + 'a iarray -> + local_ 'b iarray -> + local_ 'c iarray +(** The locally-allocating and second-biased partly-locally-scoped version of + [map2]. *) + (** {1 Array scanning} *) -val for_all : ('a -> bool) -> 'a iarray -> bool +val for_all : local_ ('a -> bool) -> 'a iarray -> bool (** [for_all f [|a1; ...; an|]] checks if all elements of the immutable array satisfy the predicate [f]. That is, it returns [(f a1) && (f a2) && ... && (f an)]. *) -val exists : ('a -> bool) -> 'a iarray -> bool +val for_all_local : local_ (local_ 'a -> bool) -> local_ 'a iarray -> bool +(** The locally-scoped version of [for_all]. *) + +val exists : local_ ('a -> bool) -> 'a iarray -> bool (** [exists f [|a1; ...; an|]] checks if at least one element of the immutable array satisfies the predicate [f]. That is, it returns [(f a1) || (f a2) || ... || (f an)]. *) -val for_all2 : ('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool +val exists_local : local_ (local_ 'a -> bool) -> local_ 'a iarray -> bool +(** The locally-scoped version of [exists]. *) + +val for_all2 : local_ ('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool (** Same as {!for_all}, but for a two-argument predicate. @raise Invalid_argument if the two immutable arrays have different lengths. *) -val exists2 : ('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool +val for_all2_local : + local_ (local_ 'a -> local_ 'b -> bool) -> + local_ 'a iarray -> + local_ 'b iarray -> + bool +(** The locally-scoped version of [for_all2]. *) + +val for_all2_local_first : + local_ (local_ 'a -> 'b -> bool) -> local_ 'a iarray -> 'b iarray -> bool +(** The first-biased partly-locally-scoped version of [for_all2]. *) + +val for_all2_local_second : + local_ ('a -> local_ 'b -> bool) -> 'a iarray -> local_ 'b iarray -> bool +(** The second-biased partly-locally-scoped version of [for_all2]. *) + +val exists2 : local_ ('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool (** Same as {!exists}, but for a two-argument predicate. @raise Invalid_argument if the two immutable arrays have different lengths. *) -val mem : 'a -> 'a iarray -> bool +val exists2_local : + local_ (local_ 'a -> local_ 'b -> bool) -> + local_ 'a iarray -> + local_ 'b iarray -> + bool +(** The locally-scoped version of [exists2]. *) + +val exists2_local_first : + local_ (local_ 'a -> 'b -> bool) -> local_ 'a iarray -> 'b iarray -> bool +(** The first-biased partly-locally-scoped version of [exists2]. *) + +val exists2_local_second : + local_ ('a -> local_ 'b -> bool) -> 'a iarray -> local_ 'b iarray -> bool +(** The second-biased partly-locally-scoped version of [exists2]. *) + +val mem : local_ 'a -> local_ 'a iarray -> bool (** [mem a set] is true if and only if [a] is structurally equal to an element of [l] (i.e. there is an [x] in [l] such that [compare a x = 0]). *) -val memq : 'a -> 'a iarray -> bool +val memq : local_ 'a -> local_ 'a iarray -> bool (** Same as {!mem}, but uses physical equality instead of structural equality to compare list elements. *) -val find_opt : ('a -> bool) -> 'a iarray -> 'a option +val find_opt : local_ ('a -> bool) -> 'a iarray -> 'a option (** [find_opt ~f a] returns the first element of the immutable array [a] that satisfies the predicate [f], or [None] if there is no value that satisfies [f] in the array [a]. *) -val find_map : ('a -> 'b option) -> 'a iarray -> 'b option +val find_opt_local : + local_ (local_ 'a -> bool) -> local_ 'a iarray -> local_ 'a option +(** The locally-constrained and locally-allocating version of []. *) + +val find_map : local_ ('a -> 'b option) -> 'a iarray -> 'b option (** [find_map ~f a] applies [f] to the elements of [a] in order, and returns the first result of the form [Some v], or [None] if none exist. *) +val find_map_local : + local_ (local_ 'a -> local_ 'b option) -> local_ 'a iarray -> local_ 'b option +(** The locally-constrained and locally-allocating version of [find_map]. *) + +val find_map_local_input : + local_ (local_ 'a -> 'b option) -> local_ 'a iarray -> 'b option +(** The locally-constrained but globally-allocating version of [find_map]. *) + +val find_map_local_output : + local_ ('a -> local_ 'b option) -> 'a iarray -> local_ 'b option +(** The locally-allocating but global-input version of [find_map]. *) + (** {1 Arrays of pairs} *) val split : ('a * 'b) iarray -> 'a iarray * 'b iarray (** [split [:(a1,b1); ...; (an,bn):]] is [([:a1; ...; an:], [:b1; ...; bn:])]. *) +val split_local : local_ ('a * 'b) iarray -> local_ 'a iarray * 'b iarray +(** The locally-allocating version of [split]. *) + val combine : 'a iarray -> 'b iarray -> ('a * 'b) iarray (** [combine [:a1; ...; an:] [:b1; ...; bn:]] is [[:(a1,b1); ...; (an,bn):]]. Raise [Invalid_argument] if the two immutable iarrays have different lengths. *) +val combine_local : + local_ 'a iarray -> local_ 'b iarray -> local_ ('a * 'b) iarray +(** The locally-allocating version of [combine]. *) + (** {1 Sorting} *) +(* CR-someday aspectorzabusky: The comparison functions could be [local_] if we + changed [Array] *) + val sort : ('a -> 'a -> int) -> 'a iarray -> 'a iarray (** Sort an immutable array in increasing order according to a comparison function. The comparison function must return 0 if its arguments @@ -229,6 +472,12 @@ val sort : ('a -> 'a -> int) -> 'a iarray -> 'a iarray - [cmp a'.:(i) a'.:(j)] >= 0 if and only if i >= j *) +(* MISSING: Requires rewriting the sorting algorithms +val sort_local : + (local_ 'a -> local_ 'a -> int) -> local_ 'a iarray -> local_ 'a iarray +(** The locally-constrained and locally-allocating version of [sort]. *) +*) + val stable_sort : ('a -> 'a -> int) -> 'a iarray -> 'a iarray (** Same as {!sort}, but the sorting algorithm is stable (i.e. elements that compare equal are kept in their original order) and @@ -239,26 +488,54 @@ val stable_sort : ('a -> 'a -> int) -> 'a iarray -> 'a iarray faster than the current implementation of {!sort}. *) +(* MISSING: Requires rewriting the sorting algorithms +val stable_sort_local : + (local_ 'a -> local_ 'a -> int) -> local_ 'a iarray -> local_ 'a iarray +(** The locally-constrained and locally-allocating version of [stable_sort]. *) +*) + val fast_sort : ('a -> 'a -> int) -> 'a iarray -> 'a iarray (** Same as {!sort} or {!stable_sort}, whichever is faster on typical input. *) +(* MISSING: Requires rewriting the sorting algorithms +val fast_sort_local : + (local_ 'a -> local_ 'a -> int) -> local_ 'a iarray -> local_ 'a iarray +(** The locally-constrained and locally-allocating version of [fast_sort]. *) +*) + (** {1 Iterators} *) val to_seq : 'a iarray -> 'a Seq.t (** Iterate on the immutable array, in increasing order. *) +(* MISSING: No meaningful local [Seq.t]s +val to_seq_local : local_ 'a iarray -> local_ 'a Seq.t +(** The locally-allocating version of [to_seq]. *) +*) + val to_seqi : 'a iarray -> (int * 'a) Seq.t (** Iterate on the immutable array, in increasing order, yielding indices along elements. *) +(* MISSING: No meaningful local [Seq.t]s +val to_seqi_local : local_ 'a iarray -> local_ (int * 'a) Seq.t +(** The locally-allocating version of [to_seqi]. *) +*) + val of_seq : 'a Seq.t -> 'a iarray (** Create an immutable array from the generator *) +(* MISSING: No meaningful local [Seq.t]s +val of_seq_local : local_ 'a Seq.t -> local_ 'a iarray +(** The locally-allocating version of [of_seq]. *) +*) + (**/**) (** {1 Undocumented functions} *) (* The following is for system use only. Do not call directly. *) -external unsafe_get : 'a iarray -> int -> 'a = "%array_unsafe_get" +external unsafe_get : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_unsafe_get" diff --git a/ocaml/stdlib/iarrayLabels.ml b/ocaml/stdlib/iarrayLabels.ml index dc5a4439278..3983b085f6d 100644 --- a/ocaml/stdlib/iarrayLabels.ml +++ b/ocaml/stdlib/iarrayLabels.ml @@ -23,6 +23,10 @@ open! Stdlib [@@@ocaml.nolabels] +(* NOTE: Please work in iarray.ml and then copy the results here; for more + information, see the top of that file. This is a temporary state of affairs, + but for now, please copy things! *) + (* CR mshinwell: Change to "include Iarray"; we can't do this at present as it requires referencing Stdlib__Iarray which will work under the make build but not under dune. *) @@ -32,86 +36,324 @@ type +'a t = 'a iarray (* Array operations *) -external length : 'a iarray -> int = "%array_length" -external get : 'a iarray -> int -> 'a = "%array_safe_get" -external ( .:() ) : 'a iarray -> int -> 'a = "%array_safe_get" -external unsafe_get : 'a iarray -> int -> 'a = "%array_unsafe_get" +external length : local_ 'a iarray -> int = "%array_length" +external get : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_safe_get" +external ( .:() ) : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_safe_get" +external unsafe_get : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_unsafe_get" external concat : 'a iarray list -> 'a iarray = "caml_array_concat" +external concat_local : local_ 'a iarray list -> local_ 'a iarray = + "caml_array_concat_local" external append_prim : 'a iarray -> 'a iarray -> 'a iarray = "caml_array_append" +external append_prim_local : + local_ 'a iarray -> local_ 'a iarray -> local_ 'a iarray = + "caml_array_append_local" external unsafe_sub : 'a iarray -> int -> int -> 'a iarray = "caml_array_sub" +external unsafe_sub_local : local_ 'a iarray -> int -> int -> local_ 'a iarray = + "caml_array_sub_local" external unsafe_of_array : 'a array -> 'a iarray = "%array_to_iarray" external unsafe_to_array : 'a iarray -> 'a array = "%array_of_iarray" -let init l f = unsafe_of_array (Array.init l f) +(* Used only to reimplement [init] *) +external unsafe_set_mutable : 'a array -> int -> 'a -> unit = + "%array_unsafe_set" + +(* VERY UNSAFE: Any of these functions can be used to violate the "no forward + pointers" restriction for the local stack if not used carefully. Each of + these can either make a local mutable array or mutate its contents, and if + not careful, this can lead to an array's contents pointing forwards. *) +external make_mutable_local : int -> local_ 'a -> local_ 'a array = + "caml_make_local_vect" +external unsafe_of_local_array : local_ 'a array -> local_ 'a iarray = + "%array_to_iarray" +external unsafe_set_local : local_ 'a array -> int -> local_ 'a -> unit = + "%array_unsafe_set" + +(* We can't use immutable array literals in this file, since we don't want to + require the stdlib to be compiled with extensions, so instead of [[::]] we + use [unsafe_of_array [||]] below. Thankfully, we never need it in the + [local] case so we don't have to think about the details. *) + +(* Really trusting the inliner here; to get maximum performance, it has to + inline both [unsafe_init_local] *and* [f]. *) +(** Precondition: [l >= 0]. *) +let[@inline always] unsafe_init_local l (local_ f : int -> local_ 'a) = local_ + if l = 0 then + unsafe_of_local_array [||] + else + (* The design of this function is exceedingly delicate, and is the only way + we can correctly allocate a local array on the stack via mutation. We + are subject to the "no forward pointers" constraint on the local stack; + we're not allowed to make pointers to later-allocated objects even within + the same stack frame. Thus, in order to get this right, we consume O(n) + call-stack space: we allocate the values to put in the array, and only + *then* recurse, creating the array as the very last thing of all and + *returning* it. This is why the [f i] call is the first thing in the + function, and why it's not tail-recursive; if it were tail-recursive, + then we wouldn't have anywhere to put the array elements during the whole + process. *) + let rec go i = local_ begin + let x = f i in + if i = l - 1 then + make_mutable_local l x + else begin + let res = go (i+1) in + unsafe_set_local res i x; + res + end + end in + unsafe_of_local_array (go 0) + +(* The implementation is copied from [Array] so that [f] can be [local_] *) +let init l (local_ f) = + if l = 0 then unsafe_of_array [||] else + if l < 0 then invalid_arg "Iarray.init" + (* See #6575. We could also check for maximum array size, but this depends + on whether we create a float array or a regular one... *) + else + let res = Array.make l (f 0) in + for i = 1 to pred l do + unsafe_set_mutable res i (f i) + done; + unsafe_of_array res + +let init_local l f = local_ + if l < 0 then invalid_arg "Iarray.init_local" + (* See #6575. We could also check for maximum array size, but this depends + on whether we create a float array or a regular one... *) + else unsafe_init_local l f let append a1 a2 = if length a1 = 0 then a2 (* Safe because they're immutable *) else if length a2 = 0 then a1 else append_prim a1 a2 +let append_local a1 a2 = local_ + if length a1 = 0 then a2 (* Safe because they're immutable *) + else if length a2 = 0 then a1 + else append_prim_local a1 a2 + let sub a ofs len = if ofs < 0 || len < 0 || ofs > length a - len then invalid_arg "Iarray.sub" else unsafe_sub a ofs len +let sub_local a ofs len = local_ + if ofs < 0 || len < 0 || ofs > length a - len + then invalid_arg "Iarray.sub" + else unsafe_sub_local a ofs len + let iter f a = for i = 0 to length a - 1 do f(unsafe_get a i) done +let iter_local f a = + for i = 0 to length a - 1 do f(unsafe_get a i) done + let iter2 f a b = if length a <> length b then invalid_arg "Iarray.iter2: arrays must have the same length" else for i = 0 to length a - 1 do f (unsafe_get a i) (unsafe_get b i) done +let iter2_local f a b = + if length a <> length b then + invalid_arg "Iarray.iter2_local: arrays must have the same length" + else + for i = 0 to length a - 1 do f (unsafe_get a i) (unsafe_get b i) done + +let iter2_local_first f a b = + if length a <> length b then + invalid_arg "Iarray.iter2_local_first: arrays must have the same length" + else + for i = 0 to length a - 1 do f (unsafe_get a i) (unsafe_get b i) done + +let iter2_local_second f a b = + if length a <> length b then + invalid_arg "Iarray.iter2_local_second: arrays must have the same length" + else + for i = 0 to length a - 1 do f (unsafe_get a i) (unsafe_get b i) done + let map f a = let l = length a in - let r = if l = 0 then [||] else begin + if l = 0 then unsafe_of_array [||] else begin let r = Array.make l (f(unsafe_get a 0)) in for i = 1 to l - 1 do Array.unsafe_set r i (f(unsafe_get a i)) done; - r - end in - unsafe_of_array r + unsafe_of_array r + end + +let map_local f a = local_ + unsafe_init_local (length a) (fun i -> local_ f (unsafe_get a i)) + +let map_local_input f a = + let l = length a in + if l = 0 then unsafe_of_array [||] else begin + let r = Array.make l (f(unsafe_get a 0)) in + for i = 1 to l - 1 do + Array.unsafe_set r i (f(unsafe_get a i)) + done; + unsafe_of_array r + end + +let map_local_output f a = local_ + unsafe_init_local (length a) (fun i -> local_ f (unsafe_get a i)) let map2 f a b = let la = length a in let lb = length b in if la <> lb then - invalid_arg "Array.map2: arrays must have the same length" + invalid_arg "Iarray.map2: arrays must have the same length" else begin - let r = if la = 0 then [||] else begin + if la = 0 then unsafe_of_array [||] else begin let r = Array.make la (f (unsafe_get a 0) (unsafe_get b 0)) in for i = 1 to la - 1 do Array.unsafe_set r i (f (unsafe_get a i) (unsafe_get b i)) done; - r - end in - unsafe_of_array r + unsafe_of_array r + end + end + +let map2_local f a b = local_ + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2_local: arrays must have the same length" + else + unsafe_init_local la (fun i -> local_ f (unsafe_get a i) (unsafe_get b i)) + +let map2_local_inputs f a b = + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2: arrays must have the same length" + else begin + if la = 0 then unsafe_of_array [||] else begin + let r = Array.make la (f (unsafe_get a 0) (unsafe_get b 0)) in + for i = 1 to la - 1 do + Array.unsafe_set r i (f (unsafe_get a i) (unsafe_get b i)) + done; + unsafe_of_array r + end end +let map2_local_output f a b = local_ + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2_local: arrays must have the same length" + else + unsafe_init_local la (fun i -> local_ f (unsafe_get a i) (unsafe_get b i)) + +let map2_local_first_input f a b = + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2: arrays must have the same length" + else begin + if la = 0 then unsafe_of_array [||] else begin + let r = Array.make la (f (unsafe_get a 0) (unsafe_get b 0)) in + for i = 1 to la - 1 do + Array.unsafe_set r i (f (unsafe_get a i) (unsafe_get b i)) + done; + unsafe_of_array r + end + end + +let map2_local_second_input f a b = + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2: arrays must have the same length" + else begin + if la = 0 then unsafe_of_array [||] else begin + let r = Array.make la (f (unsafe_get a 0) (unsafe_get b 0)) in + for i = 1 to la - 1 do + Array.unsafe_set r i (f (unsafe_get a i) (unsafe_get b i)) + done; + unsafe_of_array r + end + end + +let map2_local_first_input_and_output f a b = local_ + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2_local: arrays must have the same length" + else + unsafe_init_local la (fun i -> local_ f (unsafe_get a i) (unsafe_get b i)) + +let map2_local_second_input_and_output f a b = local_ + let la = length a in + let lb = length b in + if la <> lb then + invalid_arg "Iarray.map2_local: arrays must have the same length" + else + unsafe_init_local la (fun i -> local_ f (unsafe_get a i) (unsafe_get b i)) + let iteri f a = for i = 0 to length a - 1 do f i (unsafe_get a i) done +let iteri_local f a = + for i = 0 to length a - 1 do f i (unsafe_get a i) done + let mapi f a = let l = length a in - let r = if l = 0 then [||] else begin + if l = 0 then unsafe_of_array [||] else begin let r = Array.make l (f 0 (unsafe_get a 0)) in for i = 1 to l - 1 do Array.unsafe_set r i (f i (unsafe_get a i)) done; - r - end in - unsafe_of_array r + unsafe_of_array r + end + +let mapi_local f a = local_ + unsafe_init_local (length a) (fun i -> local_ f i (unsafe_get a i)) + +let mapi_local_input f a = + let l = length a in + if l = 0 then unsafe_of_array [||] else begin + let r = Array.make l (f 0 (unsafe_get a 0)) in + for i = 1 to l - 1 do + Array.unsafe_set r i (f i (unsafe_get a i)) + done; + unsafe_of_array r + end + +let mapi_local_output f a = local_ + unsafe_init_local (length a) (fun i -> local_ f i (unsafe_get a i)) let to_list a = let rec tolist i res = if i < 0 then res else tolist (i - 1) (unsafe_get a i :: res) in tolist (length a - 1) [] +let to_list_local a = local_ + let rec tolist i res = local_ + if i < 0 then res else tolist (i - 1) (unsafe_get a i :: res) in + tolist (length a - 1) [] + let of_list l = unsafe_of_array (Array.of_list l) +(* Cannot use List.length here because the List module depends on Array. *) +let rec list_length accu = function + | [] -> accu + | _::t -> list_length (succ accu) t + +(* This shouldn't violate the forward-pointers restriction because the list + elements already exist *) +let of_list_local = function + | [] -> local_ unsafe_of_array [||] + | hd::tl as l -> local_ + let a = make_mutable_local (list_length 0 l) hd in + let rec fill i = function + | [] -> local_ a + | hd::tl -> local_ unsafe_set_local a i hd; fill (i+1) tl in + unsafe_of_local_array (fill 1 tl) + let to_array ia = Array.copy (unsafe_to_array ia) let of_array ma = unsafe_of_array (Array.copy ma) @@ -123,9 +365,34 @@ let fold_left f x a = done; !r +let fold_left_local f x a = local_ + let len = length a in + let rec go r i = local_ + if i = len + then r + else go (f r (unsafe_get a i)) (i+1) + in + go x 0 + +let fold_left_local_input f x a = + let r = ref x in + for i = 0 to length a - 1 do + r := f !r (unsafe_get a i) + done; + !r + +let fold_left_local_output f x a = local_ + let len = length a in + let rec go r i = local_ + if i = len + then r + else go (f r (unsafe_get a i)) (i+1) + in + go x 0 + let fold_left_map f acc input_array = let len = length input_array in - let acc, output_array = if len = 0 then (acc, [||]) else begin + if len = 0 then (acc, unsafe_of_array [||]) else begin let acc, elt = f acc (unsafe_get input_array 0) in let output_array = Array.make len elt in let acc = ref acc in @@ -134,9 +401,56 @@ let fold_left_map f acc input_array = acc := acc'; Array.unsafe_set output_array i elt; done; - !acc, output_array - end in - acc, unsafe_of_array output_array + !acc, unsafe_of_array output_array + end + +let fold_left_map_local f acc input_array = local_ + let len = length input_array in + if len = 0 then (acc, unsafe_of_local_array [||]) else begin + let rec go acc i = local_ + let acc', elt = f acc (unsafe_get input_array i) in + if i = len - 1 then + acc', make_mutable_local len elt + else begin + let (_, output_array) as res = go acc (i+1) in + unsafe_set_local output_array i elt; + res + end + in + let acc, output_array = go acc 0 in + acc, unsafe_of_local_array output_array + end + +let fold_left_map_local_input f acc input_array = + let len = length input_array in + if len = 0 then (acc, unsafe_of_array [||]) else begin + let acc, elt = f acc (unsafe_get input_array 0) in + let output_array = Array.make len elt in + let acc = ref acc in + for i = 1 to len - 1 do + let acc', elt = f !acc (unsafe_get input_array i) in + acc := acc'; + Array.unsafe_set output_array i elt; + done; + !acc, unsafe_of_array output_array + end + +let fold_left_map_local_output f acc input_array = local_ + let len = length input_array in + if len = 0 then (acc, unsafe_of_local_array [||]) else begin + let rec go acc i = local_ + let acc', elt = f acc (unsafe_get input_array i) in + if i = len - 1 then + acc', make_mutable_local len elt + else begin + let (_, output_array) as res = go acc (i+1) in + unsafe_set_local output_array i elt; + res + end + in + let acc, output_array = go acc 0 in + acc, unsafe_of_local_array output_array + end let fold_right f a x = let r = ref x in @@ -145,57 +459,160 @@ let fold_right f a x = done; !r +let fold_right_local f a x = local_ + let rec go r i = local_ + if i = -1 + then r + else go (f (unsafe_get a i) r) (i-1) + in + go x (length a - 1) + +let fold_right_local_input f a x = + let r = ref x in + for i = length a - 1 downto 0 do + r := f (unsafe_get a i) !r + done; + !r + +let fold_right_local_output f a x = local_ + let rec go r i = local_ + if i = -1 + then r + else go (f (unsafe_get a i) r) (i-1) + in + go x (length a - 1) + +(* CR aspectorzabusky: Why do I need this? Shouldn't mode-crossing handle doing + this? *) +let[@inline always] globalize_bool : local_ bool -> bool = fun b -> b + let exists p a = let n = length a in - let rec loop i = + let rec loop i = local_ if i = n then false else if p (unsafe_get a i) then true else loop (succ i) in - loop 0 + globalize_bool (loop 0) + +let exists_local p a = + let n = length a in + let rec loop i = local_ + if i = n then false + else if p (unsafe_get a i) then true + else loop (succ i) in + globalize_bool (loop 0) let for_all p a = let n = length a in - let rec loop i = + let rec loop i = local_ if i = n then true else if p (unsafe_get a i) then loop (succ i) else false in - loop 0 + globalize_bool (loop 0) + +let for_all_local p a = + let n = length a in + let rec loop i = local_ + if i = n then true + else if p (unsafe_get a i) then loop (succ i) + else false in + globalize_bool (loop 0) let for_all2 p l1 l2 = let n1 = length l1 and n2 = length l2 in if n1 <> n2 then invalid_arg "Iarray.for_all2" - else let rec loop i = + else let rec loop i = local_ if i = n1 then true else if p (unsafe_get l1 i) (unsafe_get l2 i) then loop (succ i) else false in - loop 0 + globalize_bool (loop 0) + +let for_all2_local p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.for_all2_local" + else let rec loop i = local_ + if i = n1 then true + else if p (unsafe_get l1 i) (unsafe_get l2 i) then loop (succ i) + else false in + globalize_bool (loop 0) + +let for_all2_local_first p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.for_all2_local_first" + else let rec loop i = local_ + if i = n1 then true + else if p (unsafe_get l1 i) (unsafe_get l2 i) then loop (succ i) + else false in + globalize_bool (loop 0) + +let for_all2_local_second p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.for_all2_local_second" + else let rec loop i = local_ + if i = n1 then true + else if p (unsafe_get l1 i) (unsafe_get l2 i) then loop (succ i) + else false in + globalize_bool (loop 0) let exists2 p l1 l2 = let n1 = length l1 and n2 = length l2 in if n1 <> n2 then invalid_arg "Iarray.exists2" - else let rec loop i = + else let rec loop i = local_ if i = n1 then false else if p (unsafe_get l1 i) (unsafe_get l2 i) then true else loop (succ i) in - loop 0 + globalize_bool (loop 0) + +let exists2_local p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.exists2_local" + else let rec loop i = local_ + if i = n1 then false + else if p (unsafe_get l1 i) (unsafe_get l2 i) then true + else loop (succ i) in + globalize_bool (loop 0) + +let exists2_local_first p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.exists2_local_first" + else let rec loop i = local_ + if i = n1 then false + else if p (unsafe_get l1 i) (unsafe_get l2 i) then true + else loop (succ i) in + globalize_bool (loop 0) + +let exists2_local_second p l1 l2 = + let n1 = length l1 + and n2 = length l2 in + if n1 <> n2 then invalid_arg "Iarray.exists2_local_second" + else let rec loop i = local_ + if i = n1 then false + else if p (unsafe_get l1 i) (unsafe_get l2 i) then true + else loop (succ i) in + globalize_bool (loop 0) let mem x a = let n = length a in - let rec loop i = + let rec loop i = local_ if i = n then false else if compare (unsafe_get a i) x = 0 then true else loop (succ i) in - loop 0 + globalize_bool (loop 0) let memq x a = let n = length a in - let rec loop i = + let rec loop i = local_ if i = n then false else if x == (unsafe_get a i) then true else loop (succ i) in - loop 0 + globalize_bool (loop 0) let find_opt p a = let n = length a in @@ -206,6 +623,17 @@ let find_opt p a = if p x then Some x else loop (succ i) in + loop 0 [@nontail] + +let find_opt_local p a = local_ + let n = length a in + let rec loop i = local_ + if i = n then None + else + let x = unsafe_get a i in + if p x then Some x + else loop (succ i) + in loop 0 let find_map f a = @@ -217,12 +645,44 @@ let find_map f a = | None -> loop (succ i) | Some _ as r -> r in + loop 0 [@nontail] + +let find_map_local f a = local_ + let n = length a in + let rec loop i = local_ + if i = n then None + else + match f (unsafe_get a i) with + | None -> loop (succ i) + | Some _ as r -> r + in + loop 0 + +let find_map_local_input f a = + let n = length a in + let rec loop i = + if i = n then None + else + match f (unsafe_get a i) with + | None -> loop (succ i) + | Some _ as r -> r + in + loop 0 [@nontail] + +let find_map_local_output f a = local_ + let n = length a in + let rec loop i = local_ + if i = n then None + else + match f (unsafe_get a i) with + | None -> loop (succ i) + | Some _ as r -> r + in loop 0 let split x = - (* We can't use immutable array literals here, since we don't want to require - the stdlib to be compiled with extensions *) - let r1, r2 = if x = unsafe_of_array [||] then [||], [||] + if x = unsafe_of_array [||] + then unsafe_of_array [||], unsafe_of_array [||] else begin let a0, b0 = unsafe_get x 0 in let n = length x in @@ -233,14 +693,32 @@ let split x = Array.unsafe_set a i ai; Array.unsafe_set b i bi done; - a, b - end in - unsafe_of_array r1, unsafe_of_array r2 + unsafe_of_array a, unsafe_of_array b + end + +(* This shouldn't violate the forward-pointers restriction because the array + elements already exist. (This doesn't work for [combine], where we need to + create the tuples.) *) +let split_local x = local_ + if x = unsafe_of_array [||] + then unsafe_of_array [||], unsafe_of_array [||] + else begin + let a0, b0 = unsafe_get x 0 in + let n = length x in + let a = make_mutable_local n a0 in + let b = make_mutable_local n b0 in + for i = 1 to n - 1 do + let ai, bi = unsafe_get x i in + unsafe_set_local a i ai; + unsafe_set_local b i bi + done; + unsafe_of_local_array a, unsafe_of_local_array b + end let combine a b = let na = length a in let nb = length b in - if na <> nb then invalid_arg "Array.combine"; + if na <> nb then invalid_arg "Iarray.combine"; let r = if na = 0 then [||] else begin let x = Array.make na (unsafe_get a 0, unsafe_get b 0) in @@ -251,6 +729,12 @@ let combine a b = end in unsafe_of_array r +let combine_local a b = local_ + let na = length a in + let nb = length b in + if na <> nb then invalid_arg "Iarray.combine_local"; + unsafe_init_local na (fun i -> local_ unsafe_get a i, unsafe_get b i) + (* Must be fully applied due to the value restriction *) let lift_sort sorter cmp iarr = let arr = to_array iarr in diff --git a/ocaml/stdlib/iarrayLabels.mli b/ocaml/stdlib/iarrayLabels.mli index 7e9d0567dc1..b4f895b0750 100644 --- a/ocaml/stdlib/iarrayLabels.mli +++ b/ocaml/stdlib/iarrayLabels.mli @@ -25,9 +25,6 @@ open! Stdlib iarrayLabels.mli instead. *) -(* If you update any types in this module, you need to update iarray.ml as well; - it uses Obj.magic, so changes won't be detected. *) - (** Operations on immutable arrays. This module mirrors the API of [Array], but omits functions that assume mutability; in addition to obviously mutating functions, it omits [copy] along with the functions [make], [create_float], @@ -38,10 +35,11 @@ open! Stdlib type +'a t = 'a iarray (** An alias for the type of immutable arrays. *) -external length : 'a iarray -> int = "%array_length" +external length : local_ 'a iarray -> int = "%array_length" (** Return the length (number of elements) of the given immutable array. *) -external get : 'a iarray -> int -> 'a = "%array_safe_get" +external get : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_safe_get" (** [get a n] returns the element number [n] of immutable array [a]. The first element has number 0. The last element has number [length a - 1]. @@ -50,10 +48,11 @@ external get : 'a iarray -> int -> 'a = "%array_safe_get" @raise Invalid_argument if [n] is outside the range 0 to [(length a - 1)]. *) -external ( .:() ) : 'a iarray -> int -> 'a = "%array_safe_get" +external ( .:() ) : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) + = "%array_safe_get" (** A synonym for [get]. *) -val init : int -> f:(int -> 'a) -> 'a iarray +val init : int -> f:local_ (int -> 'a) -> 'a iarray (** [init n ~f] returns a fresh immutable array of length [n], with element number [i] initialized to the result of [f i]. In other terms, [init n ~f] tabulates the results of [f] @@ -63,15 +62,24 @@ val init : int -> f:(int -> 'a) -> 'a iarray If the return type of [f] is [float], then the maximum size is only [Sys.max_array_length / 2]. *) +val init_local : int -> f:local_ (int -> local_ 'a) -> local_ 'a iarray +(** The locally-allocating version of [init]. *) + val append : 'a iarray -> 'a iarray -> 'a iarray (** [append v1 v2] returns a fresh immutable array containing the concatenation of the immutable arrays [v1] and [v2]. @raise Invalid_argument if [length v1 + length v2 > Sys.max_array_length]. *) +val append_local : local_ 'a iarray -> local_ 'a iarray -> local_ 'a iarray +(** The locally-allocating version of [append]. *) + val concat : 'a iarray list -> 'a iarray (** Same as {!append}, but concatenates a list of immutable arrays. *) +val concat_local : local_ 'a iarray list -> local_ 'a iarray +(** The locally-allocating version of [concat]. *) + val sub : 'a iarray -> pos:int -> len:int -> 'a iarray (** [sub a ~pos ~len] returns a fresh immutable array of length [len], containing the elements number [pos] to [pos + len - 1] @@ -82,9 +90,15 @@ val sub : 'a iarray -> pos:int -> len:int -> 'a iarray designate a valid subarray of [a]; that is, if [pos < 0], or [len < 0], or [pos + len > length a]. *) +val sub_local : local_ 'a iarray -> int -> int -> local_ 'a iarray +(** The locally-allocating version of [sub]. *) + val to_list : 'a iarray -> 'a list (** [to_list a] returns the list of all the elements of [a]. *) +val to_list_local : local_ 'a iarray -> local_ 'a list +(** The locally-allocating version of []. *) + val of_list : 'a list -> 'a iarray (** [of_list l] returns a fresh immutable array containing the elements of [l]. @@ -92,6 +106,9 @@ val of_list : 'a list -> 'a iarray @raise Invalid_argument if the length of [l] is greater than [Sys.max_array_length]. *) +val of_list_local : local_ 'a list -> local_ 'a iarray +(** The locally-allocating version of [of_list]. *) + (** {1 Converting to and from mutable arrays} *) val to_array : 'a iarray -> 'a array @@ -104,111 +121,342 @@ val of_array : 'a array -> 'a iarray (** {1 Iterators} *) -val iter : f:('a -> unit) -> 'a iarray -> unit +val iter : f:local_ ('a -> unit) -> 'a iarray -> unit (** [iter ~f a] applies function [f] in turn to all the elements of [a]. It is equivalent to [f a.:(0); f a.:(1); ...; f a.:(length a - 1); ()]. *) -val iteri : f:(int -> 'a -> unit) -> 'a iarray -> unit +val iter_local : f:local_ (local_ 'a -> unit) -> local_ 'a iarray -> unit +(** The locally-scoped version of [iter]. *) + +val iteri : f:local_ (int -> 'a -> unit) -> 'a iarray -> unit (** Same as {!iter}, but the function is applied to the index of the element as first argument, and the element itself as second argument. *) -val map : f:('a -> 'b) -> 'a iarray -> 'b iarray +val iteri_local : + f:local_ (int -> local_ 'a -> unit) -> local_ 'a iarray -> unit +(** The locally-scoped version of [iteri]. *) + +val map : f:local_ ('a -> 'b) -> 'a iarray -> 'b iarray (** [map ~f a] applies function [f] to all the elements of [a], and builds an immutable array with the results returned by [f]: [[| f a.:(0); f a.:(1); ...; f a.:(length a - 1) |]]. *) -val mapi : f:(int -> 'a -> 'b) -> 'a iarray -> 'b iarray +val map_local : + f:local_ (local_ 'a -> local_ 'b) -> local_ 'a iarray -> local_ 'b iarray +(** The locally-scoped and locally-allocating version of [map]. *) + +val map_local_input : + f:local_ (local_ 'a -> 'b) -> local_ 'a iarray -> 'b iarray +(** The locally-constrained but globally-allocating version of [map]. *) + +val map_local_output : + f:local_ ('a -> local_ 'b) -> 'a iarray -> local_ 'b iarray +(** The locally-allocating but global-input version of [map]. *) + +val mapi : f:local_ (int -> 'a -> 'b) -> 'a iarray -> 'b iarray (** Same as {!map}, but the function is applied to the index of the element as first argument, and the element itself as second argument. *) -val fold_left : f:('a -> 'b -> 'a) -> init:'a -> 'b iarray -> 'a +val mapi_local : + f:local_ (int -> local_ 'a -> local_ 'b) -> + local_ 'a iarray -> + local_ 'b iarray +(** The locally-scoped and locally-allocating version of [mapi]. *) + +val mapi_local_input : + f:local_ (int -> local_ 'a -> 'b) -> local_ 'a iarray -> 'b iarray +(** The locally-constrained but globally-allocating version of [mapi]. *) + +val mapi_local_output : + f:local_ (int -> 'a -> local_ 'b) -> 'a iarray -> local_ 'b iarray +(** The locally-allocating but global-input version of [mapi]. *) + +val fold_left : f:local_ ('a -> 'b -> 'a) -> init:'a -> 'b iarray -> 'a (** [fold_left ~f ~init a] computes [f (... (f (f init a.:(0)) a.:(1)) ...) a.:(n-1)], where [n] is the length of the immutable array [a]. *) +val fold_left_local : + f:local_ (local_ 'a -> local_ 'b -> local_ 'a) -> + init:local_ 'a -> + local_ 'b iarray -> + local_ 'a +(** The locally-constrained and locally-allocating version of [fold_left]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + +val fold_left_local_input : + f:local_ ('a -> local_ 'b -> 'a) -> init:'a -> local_ 'b iarray -> 'a +(** The locally-constrained but globally-allocating version of [fold_left]. *) + +val fold_left_local_output : + f:local_ (local_ 'a -> 'b -> local_ 'a) -> + init:local_ 'a -> + 'b iarray -> + local_ 'a +(** The locally-allocating but global-input version of [fold_left]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + val fold_left_map : - f:('a -> 'b -> 'a * 'c) -> init:'a -> 'b iarray -> 'a * 'c iarray + f:local_ ('a -> 'b -> 'a * 'c) -> init:'a -> 'b iarray -> 'a * 'c iarray (** [fold_left_map] is a combination of {!fold_left} and {!map} that threads an accumulator through calls to [f]. *) -val fold_right : f:('b -> 'a -> 'a) -> 'b iarray -> init:'a -> 'a +val fold_left_map_local : + f:local_ (local_ 'a -> local_ 'b -> local_ 'a * 'c) -> + init:local_ 'a -> + local_ 'b iarray -> + local_ 'a * 'c iarray +(** The locally-constrained and locally-allocating version of [fold_left]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + +val fold_left_map_local_input : + f:local_ ('a -> local_ 'b -> 'a * 'c) -> + init:'a -> + local_ 'b iarray -> + 'a * 'c iarray +(** The locally-constrained but globally-allocating version of [fold_left]. *) + +val fold_left_map_local_output : + f:local_ (local_ 'a -> 'b -> local_ 'a * 'c) -> + init:local_ 'a -> + 'b iarray -> + local_ 'a * 'c iarray +(** The locally-allocating but global-input version of [fold_left]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + +val fold_right : f:local_ ('b -> 'a -> 'a) -> 'b iarray -> init:'a -> 'a (** [fold_right ~f a ~init] computes [f a.:(0) (f a.:(1) ( ... (f a.:(n-1) init) ...))], where [n] is the length of the immutable array [a]. *) +val fold_right_local : + f:local_ (local_ 'b -> local_ 'a -> local_ 'a) -> + local_ 'b iarray -> + init:local_ 'a -> + local_ 'a +(** The locally-constrained and locally-allocating version of [fold_right]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + +val fold_right_local_input : + f:local_ (local_ 'b -> 'a -> 'a) -> local_ 'b iarray -> init:'a -> 'a +(** The locally-constrained but globally-allocating version of [fold_right]. *) + +val fold_right_local_output : + f:local_ ('b -> local_ 'a -> local_ 'a) -> + 'b iarray -> + init:local_ 'a -> + local_ 'a +(** The locally-allocating but global-input version of [fold_right]. + + WARNING: This function consumes O(n) extra stack space, as every intermediate + accumulator will be left on the local stack! *) + (** {1 Iterators on two arrays} *) -val iter2 : f:('a -> 'b -> unit) -> 'a iarray -> 'b iarray -> unit +val iter2 : f:local_ ('a -> 'b -> unit) -> 'a iarray -> 'b iarray -> unit (** [iter2 ~f a b] applies function [f] to all the elements of [a] and [b]. @raise Invalid_argument if the immutable arrays are not the same size. *) -val map2 : f:('a -> 'b -> 'c) -> 'a iarray -> 'b iarray -> 'c iarray +val iter2_local : + f:local_ (local_ 'a -> local_ 'b -> unit) -> + local_ 'a iarray -> + local_ 'b iarray -> + unit +(** The locally-scoped version of [iter2]. *) + +val iter2_local_first : + f:local_ (local_ 'a -> 'b -> unit) -> local_ 'a iarray -> 'b iarray -> unit +(** The first-biased partly-locally-scoped version of [iter2]. *) + +val iter2_local_second : + f:local_ ('a -> local_ 'b -> unit) -> 'a iarray -> local_ 'b iarray -> unit +(** The second-biased partly-locally-scoped version of [iter2]. *) + +val map2 : f:local_ ('a -> 'b -> 'c) -> 'a iarray -> 'b iarray -> 'c iarray (** [map2 ~f a b] applies function [f] to all the elements of [a] and [b], and builds an immutable array with the results returned by [f]: [[| f a.:(0) b.:(0); ...; f a.:(length a - 1) b.:(length b - 1)|]]. @raise Invalid_argument if the immutable arrays are not the same size. *) +val map2_local : + f:local_ (local_ 'a -> local_ 'b -> local_ 'c) -> + local_ 'a iarray -> + local_ 'b iarray -> + local_ 'c iarray +(** The locally-scoped and locally-allocating version of [map2]. *) + +val map2_local_inputs : + f:local_ (local_ 'a -> local_ 'b -> 'c) -> + local_ 'a iarray -> + local_ 'b iarray -> + 'c iarray +(** The locally-scoped but globally-allocating version of [map2]. *) + +val map2_local_output : + f:local_ ('a -> 'b -> local_ 'c) -> 'a iarray -> 'b iarray -> local_ 'c iarray +(** The locally-allocating but global-inputs version of [map2]. *) + +val map2_local_first_input : + f:local_ (local_ 'a -> 'b -> 'c) -> local_ 'a iarray -> 'b iarray -> 'c iarray +(** The first-biased partly-locally-scoped but globally-allocating version of + [map2]. *) + +val map2_local_second_input : + f:local_ ('a -> local_ 'b -> 'c) -> 'a iarray -> local_ 'b iarray -> 'c iarray +(** The second-biased partly-locally-scoped but globally-allocating version of + [map2]. *) + +val map2_local_first_input_and_output : + f:local_ (local_ 'a -> 'b -> local_ 'c) -> + local_ 'a iarray -> + 'b iarray -> + local_ 'c iarray +(** The locally-allocating and first-biased partly-locally-scoped version of + [map2]. *) + +val map2_local_second_input_and_output : + f:local_ ('a -> local_ 'b -> local_ 'c) -> + 'a iarray -> + local_ 'b iarray -> + local_ 'c iarray +(** The locally-allocating and second-biased partly-locally-scoped version of + [map2]. *) + (** {1 Array scanning} *) -val for_all : f:('a -> bool) -> 'a iarray -> bool +val for_all : f:local_ ('a -> bool) -> 'a iarray -> bool (** [for_all ~f [|a1; ...; an|]] checks if all elements of the immutable array satisfy the predicate [f]. That is, it returns [(f a1) && (f a2) && ... && (f an)]. *) -val exists : f:('a -> bool) -> 'a iarray -> bool +val for_all_local : f:local_ (local_ 'a -> bool) -> local_ 'a iarray -> bool +(** The locally-scoped version of [for_all]. *) + +val exists : f:local_ ('a -> bool) -> 'a iarray -> bool (** [exists ~f [|a1; ...; an|]] checks if at least one element of the immutable array satisfies the predicate [f]. That is, it returns [(f a1) || (f a2) || ... || (f an)]. *) -val for_all2 : f:('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool +val exists_local : f:local_ (local_ 'a -> bool) -> local_ 'a iarray -> bool +(** The locally-scoped version of [exists]. *) + +val for_all2 : f:local_ ('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool (** Same as {!for_all}, but for a two-argument predicate. @raise Invalid_argument if the two immutable arrays have different lengths. *) -val exists2 : f:('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool +val for_all2_local : + f:local_ (local_ 'a -> local_ 'b -> bool) -> + local_ 'a iarray -> + local_ 'b iarray -> + bool +(** The locally-scoped version of [for_all2]. *) + +val for_all2_local_first : + f:local_ (local_ 'a -> 'b -> bool) -> local_ 'a iarray -> 'b iarray -> bool +(** The first-biased partly-locally-scoped version of [for_all2]. *) + +val for_all2_local_second : + f:local_ ('a -> local_ 'b -> bool) -> 'a iarray -> local_ 'b iarray -> bool +(** The second-biased partly-locally-scoped version of [for_all2]. *) + +val exists2 : f:local_ ('a -> 'b -> bool) -> 'a iarray -> 'b iarray -> bool (** Same as {!exists}, but for a two-argument predicate. @raise Invalid_argument if the two immutable arrays have different lengths. *) -val mem : 'a -> set:'a iarray -> bool +val exists2_local : + f:local_ (local_ 'a -> local_ 'b -> bool) -> + local_ 'a iarray -> + local_ 'b iarray -> + bool +(** The locally-scoped version of [exists2]. *) + +val exists2_local_first : + f:local_ (local_ 'a -> 'b -> bool) -> local_ 'a iarray -> 'b iarray -> bool +(** The first-biased partly-locally-scoped version of [exists2]. *) + +val exists2_local_second : + f:local_ ('a -> local_ 'b -> bool) -> 'a iarray -> local_ 'b iarray -> bool +(** The second-biased partly-locally-scoped version of [exists2]. *) + +val mem : local_ 'a -> set:local_ 'a iarray -> bool (** [mem a ~set] is true if and only if [a] is structurally equal to an element of [l] (i.e. there is an [x] in [l] such that [compare a x = 0]). *) -val memq : 'a -> set:'a iarray -> bool +val memq : local_ 'a -> set:local_ 'a iarray -> bool (** Same as {!mem}, but uses physical equality instead of structural equality to compare list elements. *) -val find_opt : f:('a -> bool) -> 'a iarray -> 'a option +val find_opt : f:local_ ('a -> bool) -> 'a iarray -> 'a option (** [find_opt ~f a] returns the first element of the immutable array [a] that satisfies the predicate [f], or [None] if there is no value that satisfies [f] in the array [a]. *) -val find_map : f:('a -> 'b option) -> 'a iarray -> 'b option +val find_opt_local : + f:local_ (local_ 'a -> bool) -> local_ 'a iarray -> local_ 'a option +(** The locally-constrained and locally-allocating version of []. *) + +val find_map : f:local_ ('a -> 'b option) -> 'a iarray -> 'b option (** [find_map ~f a] applies [f] to the elements of [a] in order, and returns the first result of the form [Some v], or [None] if none exist. *) +val find_map_local : + f:local_ (local_ 'a -> local_ 'b option) -> + local_ 'a iarray -> + local_ 'b option +(** The locally-constrained and locally-allocating version of [find_map]. *) + +val find_map_local_input : + f:local_ (local_ 'a -> 'b option) -> local_ 'a iarray -> 'b option +(** The locally-constrained but globally-allocating version of [find_map]. *) + +val find_map_local_output : + f:local_ ('a -> local_ 'b option) -> 'a iarray -> local_ 'b option +(** The locally-allocating but global-input version of [find_map]. *) + (** {1 Arrays of pairs} *) val split : ('a * 'b) iarray -> 'a iarray * 'b iarray (** [split [:(a1,b1); ...; (an,bn):]] is [([:a1; ...; an:], [:b1; ...; bn:])]. *) +val split_local : local_ ('a * 'b) iarray -> local_ 'a iarray * 'b iarray +(** The locally-allocating version of [split]. *) + val combine : 'a iarray -> 'b iarray -> ('a * 'b) iarray (** [combine [:a1; ...; an:] [:b1; ...; bn:]] is [[:(a1,b1); ...; (an,bn):]]. Raise [Invalid_argument] if the two immutable iarrays have different lengths. *) +val combine_local : + local_ 'a iarray -> local_ 'b iarray -> local_ ('a * 'b) iarray +(** The locally-allocating version of [combine]. *) + (** {1 Sorting} *) +(* CR-someday aspectorzabusky: The comparison functions could be [local_] if we + changed [Array] *) + val sort : cmp:('a -> 'a -> int) -> 'a iarray -> 'a iarray (** Sort an immutable array in increasing order according to a comparison function. The comparison function must return 0 if its arguments @@ -235,6 +483,12 @@ val sort : cmp:('a -> 'a -> int) -> 'a iarray -> 'a iarray - [cmp a'.:(i) a'.:(j)] >= 0 if and only if i >= j *) +(* MISSING: Requires rewriting the sorting algorithms +val sort_local : + cmp:(local_ 'a -> local_ 'a -> int) -> local_ 'a iarray -> local_ 'a iarray +(** The locally-constrained and locally-allocating version of [sort]. *) +*) + val stable_sort : cmp:('a -> 'a -> int) -> 'a iarray -> 'a iarray (** Same as {!sort}, but the sorting algorithm is stable (i.e. elements that compare equal are kept in their original order) and @@ -245,26 +499,54 @@ val stable_sort : cmp:('a -> 'a -> int) -> 'a iarray -> 'a iarray faster than the current implementation of {!sort}. *) +(* MISSING: Requires rewriting the sorting algorithms +val stable_sort_local : + cmp:(local_ 'a -> local_ 'a -> int) -> local_ 'a iarray -> local_ 'a iarray +(** The locally-constrained and locally-allocating version of [stable_sort]. *) +*) + val fast_sort : cmp:('a -> 'a -> int) -> 'a iarray -> 'a iarray (** Same as {!sort} or {!stable_sort}, whichever is faster on typical input. *) +(* MISSING: Requires rewriting the sorting algorithms +val fast_sort_local : + cmp:(local_ 'a -> local_ 'a -> int) -> local_ 'a iarray -> local_ 'a iarray +(** The locally-constrained and locally-allocating version of [fast_sort]. *) +*) + (** {1 Iterators} *) val to_seq : 'a iarray -> 'a Seq.t (** Iterate on the immutable array, in increasing order. *) +(* MISSING: No meaningful local [Seq.t]s +val to_seq_local : local_ 'a iarray -> local_ 'a Seq.t +(** The locally-allocating version of [to_seq]. *) +*) + val to_seqi : 'a iarray -> (int * 'a) Seq.t (** Iterate on the immutable array, in increasing order, yielding indices along elements. *) +(* MISSING: No meaningful local [Seq.t]s +val to_seqi_local : local_ 'a iarray -> local_ (int * 'a) Seq.t +(** The locally-allocating version of [to_seqi]. *) +*) + val of_seq : 'a Seq.t -> 'a iarray (** Create an immutable array from the generator *) +(* MISSING: No meaningful local [Seq.t]s +val of_seq_local : local_ 'a Seq.t -> local_ 'a iarray +(** The locally-allocating version of [of_seq]. *) +*) + (**/**) (** {1 Undocumented functions} *) (* The following is for system use only. Do not call directly. *) -external unsafe_get : 'a iarray -> int -> 'a = "%array_unsafe_get" +external unsafe_get : ('a iarray[@local_opt]) -> int -> ('a[@local_opt]) = + "%array_unsafe_get" diff --git a/ocaml/testsuite/tests/lib-array/test_iarray.ml b/ocaml/testsuite/tests/lib-array/test_iarray.ml index 49dfa564991..d3855bf5979 100644 --- a/ocaml/testsuite/tests/lib-array/test_iarray.ml +++ b/ocaml/testsuite/tests/lib-array/test_iarray.ml @@ -430,7 +430,7 @@ Iarray.combine [::] [::];; Iarray.combine iarray [: "wrong length" :];; [%%expect{| -Exception: Invalid_argument "Array.combine". +Exception: Invalid_argument "Iarray.combine". |}];; Iarray.sort (Fun.flip Int.compare) iarray, diff --git a/ocaml/testsuite/tests/typing-local/alloc.ml b/ocaml/testsuite/tests/typing-local/alloc.ml index 5f5976dd547..9cdbbd5308f 100644 --- a/ocaml/testsuite/tests/typing-local/alloc.ml +++ b/ocaml/testsuite/tests/typing-local/alloc.ml @@ -473,6 +473,6 @@ let () = run "optionalarg" optionalarg (fun_with_optional_arg, 10); run "optionaleta" optionaleta () - -(* In debug mode, Gc.minor () checks for minor heap->local pointers *) +(* In debug mode, Gc.minor () checks for minor heap->local pointers (and + backwards local pointers, which can't occur here) *) let () = Gc.minor () diff --git a/ocaml/testsuite/tests/typing-local/float_iarray.heap.reference b/ocaml/testsuite/tests/typing-local/float_iarray.heap.reference new file mode 100644 index 00000000000..97114426aa6 --- /dev/null +++ b/ocaml/testsuite/tests/typing-local/float_iarray.heap.reference @@ -0,0 +1,4 @@ + access from literal: Allocation +access from Iarray.init: Allocation + match on literal: Allocation + match on Iarray.init: Allocation diff --git a/ocaml/testsuite/tests/typing-local/float_iarray.ml b/ocaml/testsuite/tests/typing-local/float_iarray.ml new file mode 100644 index 00000000000..59da73d9901 --- /dev/null +++ b/ocaml/testsuite/tests/typing-local/float_iarray.ml @@ -0,0 +1,74 @@ +(* TEST + flags = "-extension immutable_arrays" + * bytecode + reference = "${test_source_directory}/float_iarray.heap.reference" + * stack-allocation + ** native + reference = "${test_source_directory}/float_iarray.stack.reference" + * no-stack-allocation + ** native + reference = "${test_source_directory}/float_iarray.heap.reference" + *) + +(* Testing that local [float iarray]s don't allocate on access. This is a + question because for flat float arrays, accesses have to box the float. *) + +module Iarray = Stdlib__Iarray + +let ( .:() ) = Iarray.( .:() ) + +external opaque_local : local_ 'a -> local_ 'a = "%opaque" + +let ignore_local : local_ 'a -> unit = fun x -> + let _ = local_ opaque_local x in + () + +let run name f x = local_ + let prebefore = Gc.allocated_bytes () in + let before = Gc.allocated_bytes () in + let r = Sys.opaque_identity f x in + let after = Gc.allocated_bytes () in + let delta = + int_of_float ((after -. before) -. (before -. prebefore)) + / (Sys.word_size/8) + in + let msg = + match delta with + | 0 -> "No Allocation" + | n -> "Allocation" + in + Printf.printf "%23s: %s\n" name msg; + r + +(* Testing functions *) + +let test_access : local_ float iarray -> local_ float = + fun iarr -> local_ iarr.:(0) + +let test_match : local_ float iarray -> local_ float = + fun iarr -> + match iarr with + | [: _; two; _ :] -> two + | _ -> assert false + +(* Run the test, keeping the values alive *) +let () = + let local_ r0 = run "access from literal" + test_access + [: 2.7; 3.1; 1.0 :] + in + let local_ r1 = run "access from Iarray.init" + test_access + (Iarray.init_local 10 (fun i -> Float.of_int i)) + in + (* TODO: Matching currently allocates, but that should be fixed eventually *) + let local_ r2 = run "match on literal" + test_match + [: 2.7; 3.1; 1.0 :] + in + let local_ r3 = run "match on Iarray.init" + test_match + (Iarray.init_local 3 (fun i -> Float.of_int i)) + in + ignore_local (r0, r1, r2, r3); + () diff --git a/ocaml/testsuite/tests/typing-local/float_iarray.stack.reference b/ocaml/testsuite/tests/typing-local/float_iarray.stack.reference new file mode 100644 index 00000000000..902aa0b97ff --- /dev/null +++ b/ocaml/testsuite/tests/typing-local/float_iarray.stack.reference @@ -0,0 +1,4 @@ + access from literal: No Allocation +access from Iarray.init: No Allocation + match on literal: Allocation + match on Iarray.init: Allocation diff --git a/ocaml/testsuite/tests/typing-local/iarray.heap.reference b/ocaml/testsuite/tests/typing-local/iarray.heap.reference new file mode 100644 index 00000000000..4987b8e6a3e --- /dev/null +++ b/ocaml/testsuite/tests/typing-local/iarray.heap.reference @@ -0,0 +1,14 @@ + init_local: Allocation + append_local: Allocation + concat_local: Allocation + sub_local: Allocation + to_list_local: Allocation + of_list_local: Allocation + map_local: Allocation + mapi_local: Allocation + fold_left_local: Allocation + fold_left_map_local: Allocation + fold_right_local: Allocation + map2_local: Allocation + split_local: Allocation + combine_local: Allocation diff --git a/ocaml/testsuite/tests/typing-local/iarray.ml b/ocaml/testsuite/tests/typing-local/iarray.ml new file mode 100644 index 00000000000..8e5d0b4615f --- /dev/null +++ b/ocaml/testsuite/tests/typing-local/iarray.ml @@ -0,0 +1,144 @@ +(* TEST + flags = "-extension immutable_arrays" + * bytecode + reference = "${test_source_directory}/iarray.heap.reference" + * stack-allocation + ** native + reference = "${test_source_directory}/iarray.stack.reference" + * no-stack-allocation + ** native + reference = "${test_source_directory}/iarray.heap.reference" + *) + +(* Testing all the [iarray] functions that allocate [iarray]s locally (not + including multiple variants of the same function) for: + + 1. Safety: No forward pointers on the local stack. + + 2. Correctness: They actually create arrays on the stack (by testing that no + GCed allocation happens). *) + +module Iarray = Stdlib__Iarray + +external opaque_local : local_ 'a -> local_ 'a = "%opaque" + +let ignore_local : local_ 'a -> unit = fun x -> + let _ = local_ opaque_local x in + () + +let local_some : 'a -> local_ 'a option = fun x -> local_ Some x + +let run name f x = local_ + let prebefore = Gc.allocated_bytes () in + let before = Gc.allocated_bytes () in + let r = Sys.opaque_identity f x in + let after = Gc.allocated_bytes () in + let delta = + int_of_float ((after -. before) -. (before -. prebefore)) + / (Sys.word_size/8) + in + let msg = + match delta with + | 0 -> "No Allocation" + | n -> "Allocation" + in + Printf.printf "%20s: %s\n" name msg; + r + +(* Testing functions *) + +let test_init n = local_ Iarray.init_local n local_some + +let test_append (a1, a2) = local_ Iarray.append_local a1 a2 + +let test_concat = Iarray.concat_local + +let test_sub a = local_ Iarray.sub_local a 0 3 + +let test_to_list = Iarray.to_list_local + +let test_of_list = Iarray.of_list_local + +let test_map a = local_ + Iarray.map_local + (function Some x -> local_ Some (-x) | None -> local_ Some 0) + a + +let test_mapi a = local_ Iarray.mapi_local (fun i x -> local_ i, x) a + +let test_fold_left a = local_ + Iarray.fold_left_local (fun l x -> local_ x :: l) [] a + +let test_fold_left_map a = local_ + Iarray.fold_left_map_local (fun l x -> local_ x :: l, Some x) [] a + +let test_fold_right a = local_ + Iarray.fold_right_local (fun x l -> local_ x :: l) a [] + +let test_map2 (a1, a2) = local_ Iarray.map2_local (fun x y -> local_ x, y) a1 a2 + +let test_split = Iarray.split_local + +let test_combine (a1, a2) = local_ Iarray.combine_local a1 a2 + +(* Run the test, keeping the values alive *) +let () = + let local_ r0 = run "init_local" + test_init + 42 + in + let local_ r1 = run "append_local" + test_append + (Iarray.init_local 42 local_some, [:None; Some (-1); Some (-2):]) + in + let local_ r2 = run "concat_local" + test_concat + [Iarray.init_local 42 local_some; [:None; Some (-1); Some (-2):]] + in + let local_ r3 = run "sub_local" + test_sub + (Iarray.init_local 42 local_some) + in + let local_ r4 = run "to_list_local" + test_to_list (Iarray.init_local 42 local_some) + in + let local_ r5 = run "of_list_local" + test_of_list [Some 0; Some 1; Some 2; Some 3; Some 4; Some 5] + in + let local_ r6 = run "map_local" + test_map + (Iarray.init_local 42 local_some) + in + let local_ r7 = run "mapi_local" + test_mapi + (Iarray.init_local 42 local_some) + in + let local_ r8 = run "fold_left_local" + test_fold_left + (Iarray.init_local 42 local_some) + in + let local_ r9 = run "fold_left_map_local" + test_fold_left_map + (Iarray.init_local 42 local_some) + in + let local_ rA = run "fold_right_local" + test_fold_right + (Iarray.init_local 42 local_some) + in + let local_ rB = run "map2_local" + test_map2 + (Iarray.init_local 3 local_some, [:None; Some (-1); Some (-2):]) + in + let local_ rC = run "split_local" + test_split + (Iarray.init_local 42 (fun i -> local_ Some i, Some (-i))) + in + let local_ rD = run "combine_local" + test_combine + (Iarray.init_local 3 local_some, [:None; Some (-1); Some (-2):]) + in + (* In debug mode, Gc.minor () checks for backwards local pointers and minor + heap->local pointers (though we're more concerned about the former) *) + Gc.minor (); + ignore_local (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, rA, rB, rC, rD); + () diff --git a/ocaml/testsuite/tests/typing-local/iarray.stack.reference b/ocaml/testsuite/tests/typing-local/iarray.stack.reference new file mode 100644 index 00000000000..be3c2c3f360 --- /dev/null +++ b/ocaml/testsuite/tests/typing-local/iarray.stack.reference @@ -0,0 +1,14 @@ + init_local: No Allocation + append_local: No Allocation + concat_local: No Allocation + sub_local: No Allocation + to_list_local: No Allocation + of_list_local: No Allocation + map_local: No Allocation + mapi_local: No Allocation + fold_left_local: No Allocation + fold_left_map_local: No Allocation + fold_right_local: No Allocation + map2_local: No Allocation + split_local: No Allocation + combine_local: No Allocation diff --git a/ocaml/typing/typecore.ml b/ocaml/typing/typecore.ml index ce53bc2703b..343a01c01c4 100644 --- a/ocaml/typing/typecore.ml +++ b/ocaml/typing/typecore.ml @@ -4800,7 +4800,6 @@ and type_expect_ ~expected_mode ~ty_expected ~explanation - ~type_:Predef.type_array ~mutability:Mutable ~attributes:sexp.pexp_attributes sargl @@ -7234,19 +7233,21 @@ and type_generic_array ~expected_mode ~ty_expected ~explanation - ~type_ ~mutability ~attributes sargl = + let type_, base_argument_mode = match mutability with + | Mutable -> Predef.type_array, mode_default Value_mode.global + | Immutable -> Predef.type_iarray, mode_subcomponent expected_mode + in let alloc_mode = register_allocation expected_mode in (* CR layouts v4: non-values in arrays *) let ty = newgenvar (Layout.value ~why:Array_element) in let to_unify = type_ ty in with_explanation explanation (fun () -> unify_exp_types loc env to_unify (generic_instance ty_expected)); - let argument_mode = mode_default Value_mode.global in - let argument_mode = expect_mode_cross env ty argument_mode in + let argument_mode = expect_mode_cross env ty base_argument_mode in let argl = List.map (fun sarg -> type_expect env argument_mode sarg (mk_expected ty)) @@ -7522,7 +7523,6 @@ and type_immutable_array ~expected_mode ~ty_expected ~explanation - ~type_:Predef.type_iarray ~mutability:Immutable ~attributes elts From b095f24ffc013fa6b2732593d9c3f7a6d034445c Mon Sep 17 00:00:00 2001 From: Antal Spector-Zabusky Date: Wed, 15 Mar 2023 15:48:59 -0400 Subject: [PATCH 2/8] Apply local immutable arrays (ocaml-jst#156) to `middle_end` & `backend` --- backend/cmm_helpers.ml | 103 ++++++++++++---------- backend/cmm_helpers.mli | 19 ++-- backend/cmmgen.ml | 24 ++--- middle_end/clambda_primitives.ml | 22 ++++- middle_end/clambda_primitives.mli | 22 ++++- middle_end/closure/closure.ml | 8 +- middle_end/convert_primitives.ml | 8 +- middle_end/flambda/inline_and_simplify.ml | 20 ++--- middle_end/flambda/inlining_cost.ml | 8 +- middle_end/printclambda_primitives.ml | 32 ++++++- middle_end/semantics_of_primitives.ml | 4 +- 11 files changed, 168 insertions(+), 102 deletions(-) diff --git a/backend/cmm_helpers.ml b/backend/cmm_helpers.ml index ff688cfb23d..009e8536f50 100644 --- a/backend/cmm_helpers.ml +++ b/backend/cmm_helpers.ml @@ -882,10 +882,10 @@ let unboxed_float_array_ref arr ofs dbg = Cop (Cload (Double, Mutable), [array_indexing log2_size_float arr ofs dbg], dbg) -let float_array_ref arr ofs dbg = - box_float dbg Lambda.alloc_heap (unboxed_float_array_ref arr ofs dbg) +let float_array_ref mode arr ofs dbg = + box_float dbg mode (unboxed_float_array_ref arr ofs dbg) -let addr_array_set arr ofs newval dbg = +let addr_array_set_heap arr ofs newval dbg = Cop ( Cextcall { func = "caml_modify"; @@ -915,6 +915,25 @@ let addr_array_set_local arr ofs newval dbg = [arr; untag_int ofs dbg; newval], dbg ) +let addr_array_set (mode : Lambda.modify_mode) arr ofs newval dbg = + match mode with + | Modify_heap -> addr_array_set_heap arr ofs newval dbg + | Modify_maybe_stack -> addr_array_set_local arr ofs newval dbg + +(* int and float arrays can be written to uniformly regardless of their mode *) + +let int_array_set arr ofs newval dbg = + Cop + ( Cstore (Word_int, Assignment), + [array_indexing log2_size_addr arr ofs dbg; newval], + dbg ) + +let float_array_set arr ofs newval dbg = + Cop + ( Cstore (Double, Assignment), + [array_indexing log2_size_float arr ofs dbg; newval], + dbg ) + let addr_array_initialize arr ofs newval dbg = Cop ( Cextcall @@ -930,18 +949,6 @@ let addr_array_initialize arr ofs newval dbg = [array_indexing log2_size_addr arr ofs dbg; newval], dbg ) -let int_array_set arr ofs newval dbg = - Cop - ( Cstore (Word_int, Assignment), - [array_indexing log2_size_addr arr ofs dbg; newval], - dbg ) - -let float_array_set arr ofs newval dbg = - Cop - ( Cstore (Double, Assignment), - [array_indexing log2_size_float arr ofs dbg; newval], - dbg ) - (* Get the field of a block given a possibly inconstant index *) let get_field_computed imm_or_ptr mut ~block ~index dbg = @@ -3281,9 +3288,9 @@ let bigstring_load size unsafe mode arg1 arg2 dbg = check_bound unsafe size dbg (bigstring_length ba dbg) idx (unaligned_load size ba_data idx dbg))))) -let arrayref_unsafe kind arg1 arg2 dbg = - match (kind : Lambda.array_kind) with - | Pgenarray -> +let arrayref_unsafe rkind arg1 arg2 dbg = + match (rkind : Lambda.array_ref_kind) with + | Pgenarray_ref mode -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> Cifthenelse @@ -3291,18 +3298,18 @@ let arrayref_unsafe kind arg1 arg2 dbg = dbg, addr_array_ref arr idx dbg, dbg, - float_array_ref arr idx dbg, + float_array_ref mode arr idx dbg, dbg, Any ))) - | Paddrarray -> addr_array_ref arg1 arg2 dbg - | Pintarray -> + | Paddrarray_ref -> addr_array_ref arg1 arg2 dbg + | Pintarray_ref -> (* CR mshinwell: for int/addr_array_ref move "dbg" to first arg *) int_array_ref arg1 arg2 dbg - | Pfloatarray -> float_array_ref arg1 arg2 dbg + | Pfloatarray_ref mode -> float_array_ref mode arg1 arg2 dbg -let arrayref_safe kind arg1 arg2 dbg = - match (kind : Lambda.array_kind) with - | Pgenarray -> +let arrayref_safe rkind arg1 arg2 dbg = + match (rkind : Lambda.array_ref_kind) with + | Pgenarray_ref mode -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> bind "header" (get_header_without_profinfo arr dbg) (fun hdr -> @@ -3316,7 +3323,7 @@ let arrayref_safe kind arg1 arg2 dbg = dbg, addr_array_ref arr idx dbg, dbg, - float_array_ref arr idx dbg, + float_array_ref mode arr idx dbg, dbg, Any ) ) else @@ -3331,10 +3338,10 @@ let arrayref_safe kind arg1 arg2 dbg = Csequence ( make_checkbound dbg [float_array_length_shifted hdr dbg; idx], - float_array_ref arr idx dbg ), + float_array_ref mode arr idx dbg ), dbg, Any )))) - | Paddrarray -> + | Paddrarray_ref -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> Csequence @@ -3344,7 +3351,7 @@ let arrayref_safe kind arg1 arg2 dbg = dbg; idx ], addr_array_ref arr idx dbg ))) - | Pintarray -> + | Pintarray_ref -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> Csequence @@ -3354,8 +3361,8 @@ let arrayref_safe kind arg1 arg2 dbg = dbg; idx ], int_array_ref arr idx dbg ))) - | Pfloatarray -> - box_float dbg Lambda.alloc_heap + | Pfloatarray_ref mode -> + box_float dbg mode (bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> Csequence @@ -3371,7 +3378,7 @@ type ternary_primitive = let setfield_computed ptr init arg1 arg2 arg3 dbg = match assignment_kind ptr init with - | Caml_modify -> return_unit dbg (addr_array_set arg1 arg2 arg3 dbg) + | Caml_modify -> return_unit dbg (addr_array_set_heap arg1 arg2 arg3 dbg) | Caml_modify_local -> return_unit dbg (addr_array_set_local arg1 arg2 arg3 dbg) | Caml_initialize -> @@ -3398,29 +3405,29 @@ let bytesset_safe arg1 arg2 arg3 dbg = [add_int str idx dbg; ignore_high_bit_int newval], dbg ) ))))) -let arrayset_unsafe kind arg1 arg2 arg3 dbg = +let arrayset_unsafe skind arg1 arg2 arg3 dbg = return_unit dbg - (match (kind : Lambda.array_kind) with - | Pgenarray -> + (match (skind : Lambda.array_set_kind) with + | Pgenarray_set mode -> bind "newval" arg3 (fun newval -> bind "index" arg2 (fun index -> bind "arr" arg1 (fun arr -> Cifthenelse ( is_addr_array_ptr arr dbg, dbg, - addr_array_set arr index newval dbg, + addr_array_set mode arr index newval dbg, dbg, float_array_set arr index (unbox_float dbg newval) dbg, dbg, Any )))) - | Paddrarray -> addr_array_set arg1 arg2 arg3 dbg - | Pintarray -> int_array_set arg1 arg2 arg3 dbg - | Pfloatarray -> float_array_set arg1 arg2 arg3 dbg) + | Paddrarray_set mode -> addr_array_set mode arg1 arg2 arg3 dbg + | Pintarray_set -> int_array_set arg1 arg2 arg3 dbg + | Pfloatarray_set -> float_array_set arg1 arg2 arg3 dbg) -let arrayset_safe kind arg1 arg2 arg3 dbg = +let arrayset_safe skind arg1 arg2 arg3 dbg = return_unit dbg - (match (kind : Lambda.array_kind) with - | Pgenarray -> + (match (skind : Lambda.array_set_kind) with + | Pgenarray_set mode -> bind "newval" arg3 (fun newval -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> @@ -3434,7 +3441,7 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = Cifthenelse ( is_addr_array_hdr hdr dbg, dbg, - addr_array_set arr idx newval dbg, + addr_array_set mode arr idx newval dbg, dbg, float_array_set arr idx (unbox_float dbg newval) dbg, @@ -3447,7 +3454,7 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = Csequence ( make_checkbound dbg [addr_array_length_shifted hdr dbg; idx], - addr_array_set arr idx newval dbg ), + addr_array_set mode arr idx newval dbg ), dbg, Csequence ( make_checkbound dbg @@ -3456,7 +3463,7 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = dbg ), dbg, Any ))))) - | Paddrarray -> + | Paddrarray_set mode -> bind "newval" arg3 (fun newval -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> @@ -3466,8 +3473,8 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = (get_header_without_profinfo arr dbg) dbg; idx ], - addr_array_set arr idx newval dbg )))) - | Pintarray -> + addr_array_set mode arr idx newval dbg )))) + | Pintarray_set -> bind "newval" arg3 (fun newval -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> @@ -3478,7 +3485,7 @@ let arrayset_safe kind arg1 arg2 arg3 dbg = dbg; idx ], int_array_set arr idx newval dbg )))) - | Pfloatarray -> + | Pfloatarray_set -> bind_load "newval" arg3 (fun newval -> bind "index" arg2 (fun idx -> bind "arr" arg1 (fun arr -> diff --git a/backend/cmm_helpers.mli b/backend/cmm_helpers.mli index eaf8ec2d54a..f763ec8a1e3 100644 --- a/backend/cmm_helpers.mli +++ b/backend/cmm_helpers.mli @@ -328,9 +328,10 @@ val int_array_ref : expression -> expression -> Debuginfo.t -> expression val unboxed_float_array_ref : expression -> expression -> Debuginfo.t -> expression -val float_array_ref : expression -> expression -> Debuginfo.t -> expression +val float_array_ref : + Lambda.alloc_mode -> expression -> expression -> Debuginfo.t -> expression -val addr_array_set : +val addr_array_set_heap : expression -> expression -> expression -> Debuginfo.t -> expression val addr_array_set_local : @@ -339,6 +340,10 @@ val addr_array_set_local : val addr_array_initialize : expression -> expression -> expression -> Debuginfo.t -> expression +val addr_array_set : + Lambda.modify_mode -> expression -> expression -> expression -> Debuginfo.t -> + expression + val int_array_set : expression -> expression -> expression -> Debuginfo.t -> expression @@ -711,9 +716,10 @@ val bigstring_load : (** Arrays *) (** Array access. Args: array, index *) -val arrayref_unsafe : Lambda.array_kind -> binary_primitive +val arrayref_unsafe : Lambda.array_ref_kind -> binary_primitive -val arrayref_safe : Lambda.array_kind -> binary_primitive +(** Array access. Args: array, index *) +val arrayref_safe : Lambda.array_ref_kind -> binary_primitive type ternary_primitive = expression -> expression -> expression -> Debuginfo.t -> expression @@ -738,9 +744,10 @@ val bytesset_safe : ternary_primitive including in the case where the array contains floats. Args: array, index, value *) -val arrayset_unsafe : Lambda.array_kind -> ternary_primitive +val arrayset_unsafe : Lambda.array_set_kind -> ternary_primitive -val arrayset_safe : Lambda.array_kind -> ternary_primitive +(** As [arrayset_unsafe], but performs bounds-checking. *) +val arrayset_safe : Lambda.array_set_kind -> ternary_primitive (** Set a chunk of data in the given bytes or bigstring structure. See also [string_load] and [bigstring_load]. diff --git a/backend/cmmgen.ml b/backend/cmmgen.ml index 5ff1b2f00d1..a138444e4ed 100644 --- a/backend/cmmgen.ml +++ b/backend/cmmgen.ml @@ -1123,10 +1123,10 @@ and transl_prim_2 env p arg1 arg2 dbg = bigstring_load size unsafe mode (transl env arg1) (transl env arg2) dbg (* Array operations *) - | Parrayrefu kind -> - arrayref_unsafe kind (transl env arg1) (transl env arg2) dbg - | Parrayrefs kind -> - arrayref_safe kind (transl env arg1) (transl env arg2) dbg + | Parrayrefu rkind -> + arrayref_unsafe rkind (transl env arg1) (transl env arg2) dbg + | Parrayrefs rkind -> + arrayref_safe rkind (transl env arg1) (transl env arg2) dbg (* Boxed integers *) | Paddbint (bi, mode) -> @@ -1211,20 +1211,20 @@ and transl_prim_3 env p arg1 arg2 arg3 dbg = (transl env arg1) (transl env arg2) (transl env arg3) dbg (* Array operations *) - | Parraysetu kind -> + | Parraysetu skind -> let newval = - match kind with - | Pfloatarray -> transl_unbox_float dbg env arg3 + match skind with + | Pfloatarray_set -> transl_unbox_float dbg env arg3 | _ -> transl env arg3 in - arrayset_unsafe kind (transl env arg1) (transl env arg2) newval dbg - | Parraysets kind -> + arrayset_unsafe skind (transl env arg1) (transl env arg2) newval dbg + | Parraysets skind -> let newval = - match kind with - | Pfloatarray -> transl_unbox_float dbg env arg3 + match skind with + | Pfloatarray_set -> transl_unbox_float dbg env arg3 | _ -> transl env arg3 in - arrayset_safe kind (transl env arg1) (transl env arg2) newval dbg + arrayset_safe skind (transl env arg1) (transl env arg2) newval dbg | Pbytes_set(size, unsafe) -> bytes_set size unsafe (transl env arg1) (transl env arg2) diff --git a/middle_end/clambda_primitives.ml b/middle_end/clambda_primitives.ml index 77e66745305..92725a7a771 100644 --- a/middle_end/clambda_primitives.ml +++ b/middle_end/clambda_primitives.ml @@ -32,6 +32,8 @@ type memory_access_size = type alloc_mode = Lambda.alloc_mode +type modify_mode = Lambda.modify_mode + type primitive = | Pread_symbol of string (* Operations on heap blocks *) @@ -74,10 +76,10 @@ type primitive = The arguments of [Pduparray] give the kind and mutability of the array being *produced* by the duplication. *) | Parraylength of array_kind - | Parrayrefu of array_kind - | Parraysetu of array_kind - | Parrayrefs of array_kind - | Parraysets of array_kind + | Parrayrefu of array_ref_kind + | Parraysetu of array_set_kind + | Parrayrefs of array_ref_kind + | Parraysets of array_set_kind (* Test if the argument is a block or an immediate integer *) | Pisint (* Test if the (integer) argument is outside an interval *) @@ -136,6 +138,18 @@ and float_comparison = Lambda.float_comparison = and array_kind = Lambda.array_kind = Pgenarray | Paddrarray | Pintarray | Pfloatarray +and array_ref_kind = Lambda.array_ref_kind = + | Pgenarray_ref of alloc_mode + | Paddrarray_ref + | Pintarray_ref + | Pfloatarray_ref of alloc_mode + +and array_set_kind = Lambda.array_set_kind = + | Pgenarray_set of modify_mode + | Paddrarray_set of modify_mode + | Pintarray_set + | Pfloatarray_set + and value_kind = Lambda.value_kind = (* CR mshinwell: Pfloatval should be renamed to Pboxedfloatval *) Pgenval | Pfloatval | Pboxedintval of boxed_integer | Pintval diff --git a/middle_end/clambda_primitives.mli b/middle_end/clambda_primitives.mli index e71b17c4c85..b65e674ee76 100644 --- a/middle_end/clambda_primitives.mli +++ b/middle_end/clambda_primitives.mli @@ -32,6 +32,8 @@ type memory_access_size = type alloc_mode = Lambda.alloc_mode +type modify_mode = Lambda.modify_mode + type primitive = | Pread_symbol of string (* Operations on heap blocks *) @@ -77,10 +79,10 @@ type primitive = The arguments of [Pduparray] give the kind and mutability of the array being *produced* by the duplication. *) | Parraylength of array_kind - | Parrayrefu of array_kind - | Parraysetu of array_kind - | Parrayrefs of array_kind - | Parraysets of array_kind + | Parrayrefu of array_ref_kind + | Parraysetu of array_set_kind + | Parrayrefs of array_ref_kind + | Parraysets of array_set_kind (* Test if the argument is a block or an immediate integer *) | Pisint (* Test if the (integer) argument is outside an interval *) @@ -139,6 +141,18 @@ and float_comparison = Lambda.float_comparison = and array_kind = Lambda.array_kind = Pgenarray | Paddrarray | Pintarray | Pfloatarray +and array_ref_kind = Lambda.array_ref_kind = + | Pgenarray_ref of alloc_mode + | Paddrarray_ref + | Pintarray_ref + | Pfloatarray_ref of alloc_mode + +and array_set_kind = Lambda.array_set_kind = + | Pgenarray_set of modify_mode + | Paddrarray_set of modify_mode + | Pintarray_set + | Pfloatarray_set + and value_kind = Lambda.value_kind = (* CR mshinwell: Pfloatval should be renamed to Pboxedfloatval *) Pgenval | Pfloatval | Pboxedintval of boxed_integer | Pintval diff --git a/middle_end/closure/closure.ml b/middle_end/closure/closure.ml index 87b13e0875b..95f7588333d 100644 --- a/middle_end/closure/closure.ml +++ b/middle_end/closure/closure.ml @@ -183,10 +183,10 @@ let prim_size prim args = | Pbytesrefs | Pbytessets -> 6 | Pmakearray _ -> 5 + List.length args | Parraylength kind -> if kind = Pgenarray then 6 else 2 - | Parrayrefu kind -> if kind = Pgenarray then 12 else 2 - | Parraysetu kind -> if kind = Pgenarray then 16 else 4 - | Parrayrefs kind -> if kind = Pgenarray then 18 else 8 - | Parraysets kind -> if kind = Pgenarray then 22 else 10 + | Parrayrefu kind -> (match kind with Pgenarray_ref _ -> 12 | _ -> 2) + | Parraysetu kind -> (match kind with Pgenarray_set _ -> 16 | _ -> 4) + | Parrayrefs kind -> (match kind with Pgenarray_ref _ -> 18 | _ -> 8) + | Parraysets kind -> (match kind with Pgenarray_set _ -> 22 | _ -> 10) | Pbigarrayref(_, ndims, _, _) -> 4 + ndims * 6 | Pbigarrayset(_, ndims, _, _) -> 4 + ndims * 6 | Pprobe_is_enabled _ -> 4 (* Pgetglobal and a comparison *) diff --git a/middle_end/convert_primitives.ml b/middle_end/convert_primitives.ml index 541af7ecf86..7cc2e8a324c 100644 --- a/middle_end/convert_primitives.ml +++ b/middle_end/convert_primitives.ml @@ -81,10 +81,10 @@ let convert (prim : Lambda.primitive) : Clambda_primitives.primitive = | Pmakearray (kind, mutability, mode) -> Pmakearray (kind, mutability, mode) | Pduparray (kind, mutability) -> Pduparray (kind, mutability) | Parraylength kind -> Parraylength kind - | Parrayrefu kind -> Parrayrefu kind - | Parraysetu kind -> Parraysetu kind - | Parrayrefs kind -> Parrayrefs kind - | Parraysets kind -> Parraysets kind + | Parrayrefu rkind -> Parrayrefu rkind + | Parraysetu skind -> Parraysetu skind + | Parrayrefs rkind -> Parrayrefs rkind + | Parraysets skind -> Parraysets skind | Pisint _ -> Pisint | Pisout -> Pisout | Pcvtbint (src, dest, m) -> Pcvtbint (src, dest, m) diff --git a/middle_end/flambda/inline_and_simplify.ml b/middle_end/flambda/inline_and_simplify.ml index 3d9e08b2f2b..7090135ee16 100644 --- a/middle_end/flambda/inline_and_simplify.ml +++ b/middle_end/flambda/inline_and_simplify.ml @@ -1103,34 +1103,34 @@ and simplify_named env r (tree : Flambda.named) : Flambda.named * R.t = end end | Pfield _, _, _ -> Misc.fatal_error "Pfield arity error" - | (Parraysetu kind | Parraysets kind), + | (Parraysetu skind | Parraysets skind), [_block; _field; _value], [block_approx; _field_approx; value_approx] -> if A.warn_on_mutation block_approx then begin Location.prerr_warning (Debuginfo.to_location dbg) Warnings.Flambda_assignment_to_non_mutable_value end; - let kind = + let skind = let check () = - match kind with - | Pfloatarray | Pgenarray -> () - | Paddrarray | Pintarray -> + match skind with + | Pfloatarray_set | Pgenarray_set _ -> () + | Paddrarray_set _ | Pintarray_set -> (* CR pchambart: Do a proper warning here *) Misc.fatal_errorf "Assignment of a float to a specialised \ non-float array: %a" Flambda.print_named tree in match A.descr block_approx, A.descr value_approx with - | (Value_float_array _, _) -> check (); Lambda.Pfloatarray + | (Value_float_array _, _) -> check (); Lambda.Pfloatarray_set | (_, Value_float _) when Config.flat_float_array -> - check (); Lambda.Pfloatarray + check (); Lambda.Pfloatarray_set (* CR pchambart: This should be accounted by the benefit *) | _ -> - kind + skind in let prim : Clambda_primitives.primitive = match prim with - | Parraysetu _ -> Parraysetu kind - | Parraysets _ -> Parraysets kind + | Parraysetu _ -> Parraysetu skind + | Parraysets _ -> Parraysets skind | _ -> assert false in Prim (prim, args, dbg), ret r (A.value_unknown Other) diff --git a/middle_end/flambda/inlining_cost.ml b/middle_end/flambda/inlining_cost.ml index fdfe65b2284..7957aa41a0c 100644 --- a/middle_end/flambda/inlining_cost.ml +++ b/middle_end/flambda/inlining_cost.ml @@ -44,13 +44,13 @@ let prim_size (prim : Clambda_primitives.primitive) args = | Pmakearray _ -> 5 + List.length args | Parraylength Pgenarray -> 6 | Parraylength _ -> 2 - | Parrayrefu Pgenarray -> 12 + | Parrayrefu (Pgenarray_ref _) -> 12 | Parrayrefu _ -> 2 - | Parraysetu Pgenarray -> 16 + | Parraysetu (Pgenarray_set _) -> 16 | Parraysetu _ -> 4 - | Parrayrefs Pgenarray -> 18 + | Parrayrefs (Pgenarray_ref _) -> 18 | Parrayrefs _ -> 8 - | Parraysets Pgenarray -> 22 + | Parraysets (Pgenarray_set _) -> 22 | Parraysets _ -> 10 | Pbigarrayref (_, ndims, _, _) -> 4 + ndims * 6 | Pbigarrayset (_, ndims, _, _) -> 4 + ndims * 6 diff --git a/middle_end/printclambda_primitives.ml b/middle_end/printclambda_primitives.ml index f8762b18c4f..b7e41451b17 100644 --- a/middle_end/printclambda_primitives.ml +++ b/middle_end/printclambda_primitives.ml @@ -41,6 +41,30 @@ let array_kind array_kind = | Pintarray -> "int" | Pfloatarray -> "float" +let pp_array_ref_kind ppf k = + let open Lambda in + let pp_mode ppf = function + | Alloc_heap -> () + | Alloc_local -> fprintf ppf "(local)" + in + match k with + | Pgenarray_ref mode -> fprintf ppf "gen%a" pp_mode mode + | Paddrarray_ref -> fprintf ppf "addr" + | Pintarray_ref -> fprintf ppf "int" + | Pfloatarray_ref mode -> fprintf ppf "float%a" pp_mode mode + +let pp_array_set_kind ppf k = + let open Lambda in + let pp_mode ppf = function + | Modify_heap -> () + | Modify_maybe_stack -> fprintf ppf "(local)" + in + match k with + | Pgenarray_set mode -> fprintf ppf "gen%a" pp_mode mode + | Paddrarray_set mode -> fprintf ppf "addr%a" pp_mode mode + | Pintarray_set -> fprintf ppf "int" + | Pfloatarray_set -> fprintf ppf "float" + let access_size size = let open Clambda_primitives in match size with @@ -171,10 +195,10 @@ let primitive ppf (prim:Clambda_primitives.primitive) = | Pduparray (k, Immutable) -> fprintf ppf "duparray_imm[%s]" (array_kind k) | Pduparray (k, Immutable_unique) -> fprintf ppf "duparray_unique[%s]" (array_kind k) - | Parrayrefu k -> fprintf ppf "array.unsafe_get[%s]" (array_kind k) - | Parraysetu k -> fprintf ppf "array.unsafe_set[%s]" (array_kind k) - | Parrayrefs k -> fprintf ppf "array.get[%s]" (array_kind k) - | Parraysets k -> fprintf ppf "array.set[%s]" (array_kind k) + | Parrayrefu rk -> fprintf ppf "array.unsafe_get[%a]" pp_array_ref_kind rk + | Parraysetu sk -> fprintf ppf "array.unsafe_set[%a]" pp_array_set_kind sk + | Parrayrefs rk -> fprintf ppf "array.get[%a]" pp_array_ref_kind rk + | Parraysets sk -> fprintf ppf "array.set[%a]" pp_array_set_kind sk | Pisint -> fprintf ppf "isint" | Pisout -> fprintf ppf "isout" | Pbintofint (bi,m) -> print_boxed_integer "of_int" ppf bi m diff --git a/middle_end/semantics_of_primitives.ml b/middle_end/semantics_of_primitives.ml index d1ffb1c0ebf..3254c463e8d 100644 --- a/middle_end/semantics_of_primitives.ml +++ b/middle_end/semantics_of_primitives.ml @@ -171,8 +171,8 @@ let return_type_of_primitive (prim:Clambda_primitives.primitive) = | Pmulfloat _ | Pdivfloat _ | Pfloatfield _ - | Parrayrefu Pfloatarray - | Parrayrefs Pfloatarray -> + | Parrayrefu (Pfloatarray_ref _) + | Parrayrefs (Pfloatarray_ref _) -> Float | _ -> Other From 8f778f7d0b9aeae595a7a1cd05d3b1b485477f06 Mon Sep 17 00:00:00 2001 From: Antal Spector-Zabusky Date: Tue, 30 May 2023 17:10:14 -0400 Subject: [PATCH 3/8] Drop suspicious claim that `Parraysetu` can allocate --- ocaml/lambda/translprim.ml | 3 --- 1 file changed, 3 deletions(-) diff --git a/ocaml/lambda/translprim.ml b/ocaml/lambda/translprim.ml index 9b5527e4fc8..c3a87af2a8c 100644 --- a/ocaml/lambda/translprim.ml +++ b/ocaml/lambda/translprim.ml @@ -936,9 +936,6 @@ let lambda_primitive_needs_event_after = function | Pbox_float _ | Pbox_int _ | Pbytessets | Pmakearray (Pgenarray, _, _) | Pduparray _ | Parrayrefu (Pgenarray_ref _ | Pfloatarray_ref _) - | Parraysetu (Pgenarray_set _ | Pfloatarray_set) - (* CR aspectorzabusky for mshinwell: It seems to be that unsafe set should - never allocate or throw an exception; am I missing something? *) | Parrayrefs _ | Parraysets _ | Pbintofint _ | Pcvtbint _ | Pnegbint _ | Paddbint _ | Psubbint _ | Pmulbint _ | Pdivbint _ | Pmodbint _ | Pandbint _ | Porbint _ | Pxorbint _ | Plslbint _ | Plsrbint _ | Pasrbint _ | Pbintcomp _ From a32069367853ea2897b500dbab1141c7169f3357 Mon Sep 17 00:00:00 2001 From: Antal Spector-Zabusky Date: Fri, 26 May 2023 18:15:49 -0400 Subject: [PATCH 4/8] Checkpoint on flambda2 implementation --- .../lambda_to_flambda_primitives.ml | 109 +++++++++++++----- .../flambda2/terms/flambda_primitive.ml | 89 +++++++++++++- .../flambda2/terms/flambda_primitive.mli | 42 ++++++- 3 files changed, 207 insertions(+), 33 deletions(-) diff --git a/middle_end/flambda2/from_lambda/lambda_to_flambda_primitives.ml b/middle_end/flambda2/from_lambda/lambda_to_flambda_primitives.ml index 46fc5bf934c..848937af7bb 100644 --- a/middle_end/flambda2/from_lambda/lambda_to_flambda_primitives.ml +++ b/middle_end/flambda2/from_lambda/lambda_to_flambda_primitives.ml @@ -131,6 +131,37 @@ let convert_array_kind (kind : L.array_kind) : converted_array_kind = | Pintarray -> Array_kind Immediates | Pfloatarray -> Array_kind Naked_floats +type converted_array_ref_kind = + | Array_ref_kind of P.Array_ref_kind.t + | Float_array_opt_dynamic_ref of Alloc_mode.For_allocations.t + +let convert_array_ref_kind + ~current_region (kind : L.array_ref_kind) : converted_array_ref_kind = + match kind with + | Pgenarray_ref mode -> + check_float_array_optimisation_enabled (); + Float_array_opt_dynamic_ref + (Alloc_mode.For_allocations.from_lambda ~current_region mode) + | Paddrarray_ref -> Array_ref_kind Values + | Pintarray_ref -> Array_ref_kind Immediates + | Pfloatarray_ref mode -> + Array_ref_kind + (Naked_floats (Alloc_mode.For_allocations.from_lambda ~current_region mode)) + +type converted_array_set_kind = + | Array_set_kind of P.Array_set_kind.t + | Float_array_opt_dynamic_set of Alloc_mode.For_assignments.t + +let convert_array_set_kind (kind : L.array_set_kind) : converted_array_set_kind = + match kind with + | Pgenarray_set mode -> + check_float_array_optimisation_enabled (); + Float_array_opt_dynamic_set (Alloc_mode.For_assignments.from_lambda mode) + | Paddrarray_set mode -> + Array_set_kind (Values (Alloc_mode.For_assignments.from_lambda mode)) + | Pintarray_set -> Array_set_kind Immediates + | Pfloatarray_set -> Array_set_kind Naked_floats + type converted_duplicate_array_kind = | Duplicate_array_kind of P.Duplicate_array_kind.t | Float_array_opt_dynamic @@ -491,20 +522,26 @@ let check_array_access ~dbg ~array ~index primitive : H.expr_primitive = ~conditions:(array_access_validity_condition array index) ~dbg -let array_load_unsafe ~array ~index (array_kind : P.Array_kind.t) - ~current_region : H.expr_primitive = - match array_kind with +let array_load_unsafe ~array ~index (array_ref_kind : P.Array_ref_kind.t) + ~current_region : H.expr_primitive = + match array_ref_kind with | Immediates | Values -> - Binary (Array_load (array_kind, Mutable), array, index) - | Naked_floats -> - box_float L.alloc_heap + Binary (Array_load (array_ref_kind, Mutable), array, index) + | Naked_floats mode -> + box_float mode (Binary (Array_load (Naked_floats, Mutable), array, index)) ~current_region -let array_set_unsafe ~array ~index ~new_value (array_kind : P.Array_kind.t) : +let array_set_unsafe ~array ~index ~new_value (array_set_kind : P.Array_set_kind.t) : H.expr_primitive = - match array_kind with - | Immediates | Values -> + match array_set_kind with + | Immediates + Ternary + ( Array_set (array_kind, Assignment Alloc_mode.For_assignments.heap), + array, + index, + new_value ) + | Values -> Ternary ( Array_set (array_kind, Assignment Alloc_mode.For_assignments.heap), array, @@ -518,17 +555,29 @@ let array_set_unsafe ~array ~index ~new_value (array_kind : P.Array_kind.t) : index, unbox_float new_value ) -let[@inline always] match_on_array_kind ~array array_kind f : H.expr_primitive = - match convert_array_kind array_kind with - | Array_kind ((Immediates | Values) as array_kind) -> f array_kind - | Array_kind Naked_floats -> f P.Array_kind.Naked_floats - | Float_array_opt_dynamic -> +let[@inline always] match_on_array_ref_kind ~current_region ~array array_ref_kind f + : H.expr_primitive = + match convert_array_ref_kind ~current_region array_ref_kind with + | Array_ref_kind array_ref_kind -> f array_ref_kind + | Float_array_opt_dynamic_ref mode -> + (* CR keryan: we should push the ITE as low as possible to avoid duplicating + too much *) + If_then_else + ( Unary (Is_flat_float_array, array), + f (P.Array_ref_kind.Naked_floats mode), + f P.Array_ref_kind.Values ) + +let[@inline always] match_on_array_set_kind ~array array_ref_kind f + : H.expr_primitive = + match convert_array_set_kind array_ref_kind with + | Array_set_kind array_set_kind -> f array_set_kind + | Float_array_opt_dynamic_set mode -> (* CR keryan: we should push the ITE as low as possible to avoid duplicating too much *) If_then_else ( Unary (Is_flat_float_array, array), - f P.Array_kind.Naked_floats, - f P.Array_kind.Values ) + f P.Array_set_kind.Naked_floats, + f (P.Array_set_kind.Values mode) ) (* Safe arith (div/mod by zero) *) let checked_arith_op ~dbg (bi : Lambda.boxed_integer option) op mode arg1 arg2 @@ -969,22 +1018,22 @@ let convert_lprim ~big_endian (prim : L.primitive) (args : Simple.t list) | Pmodbint { size = Pnativeint; is_safe = Safe; mode }, [arg1; arg2] -> checked_arith_op ~dbg (Some Pnativeint) Mod (Some mode) arg1 arg2 ~current_region - | Parrayrefu array_kind, [array; index] -> + | Parrayrefu array_ref_kind, [array; index] -> (* For this and the following cases we will end up relying on the backend to CSE the two accesses to the array's header word in the [Pgenarray] case. *) - match_on_array_kind ~array array_kind + match_on_array_ref_kind ~current_region ~array array_ref_kind (array_load_unsafe ~array ~index ~current_region) - | Parrayrefs array_kind, [array; index] -> + | Parrayrefs array_ref_kind, [array; index] -> check_array_access ~dbg ~array ~index - (match_on_array_kind ~array array_kind + (match_on_array_ref_kind ~current_region ~array array_ref_kind (array_load_unsafe ~array ~index ~current_region)) - | Parraysetu array_kind, [array; index; new_value] -> - match_on_array_kind ~array array_kind + | Parraysetu array_set_kind, [array; index; new_value] -> + match_on_array_set_kind ~array array_set_kind (array_set_unsafe ~array ~index ~new_value) - | Parraysets array_kind, [array; index; new_value] -> + | Parraysets array_set_kind, [array; index; new_value] -> check_array_access ~dbg ~array ~index - (match_on_array_kind ~array array_kind + (match_on_array_set_kind ~array array_set_kind (array_set_unsafe ~array ~index ~new_value)) | Pbytessetu (* unsafe *), [bytes; index; new_value] -> bytes_like_set_unsafe ~access_size:Eight Bytes bytes index new_value @@ -1194,8 +1243,10 @@ let convert_lprim ~big_endian (prim : L.primitive) (args : Simple.t list) | Plsrbint _ | Pasrbint _ | Pfield_computed _ | Pdivbint _ | Pmodbint _ | Psetfloatfield _ | Pbintcomp _ | Pbigstring_load_16 _ | Pbigstring_load_32 _ | Pbigstring_load_64 _ - | Parrayrefu (Pgenarray | Paddrarray | Pintarray | Pfloatarray) - | Parrayrefs (Pgenarray | Paddrarray | Pintarray | Pfloatarray) + | Parrayrefu + (Pgenarray_ref _ | Paddrarray_ref | Pintarray_ref | Pfloatarray_ref _) + | Parrayrefs + (Pgenarray_ref _ | Paddrarray_ref | Pintarray_ref | Pfloatarray_ref _) | Pcompare_ints | Pcompare_floats | Pcompare_bints _ ), ([] | [_] | _ :: _ :: _ :: _) ) -> Misc.fatal_errorf @@ -1203,8 +1254,10 @@ let convert_lprim ~big_endian (prim : L.primitive) (args : Simple.t list) %a (%a)" Printlambda.primitive prim H.print_list_of_simple_or_prim args | ( ( Psetfield_computed _ | Pbytessetu | Pbytessets - | Parraysetu (Pgenarray | Paddrarray | Pintarray | Pfloatarray) - | Parraysets (Pgenarray | Paddrarray | Pintarray | Pfloatarray) + | Parraysetu + (Pgenarray_set _ | Paddrarray_set _ | Pintarray_set | Pfloatarray_set) + | Parraysets + (Pgenarray_set _ | Paddrarray_set _ | Pintarray_set | Pfloatarray_set) | Pbytes_set_16 _ | Pbytes_set_32 _ | Pbytes_set_64 _ | Pbigstring_set_16 _ | Pbigstring_set_32 _ | Pbigstring_set_64 _ ), ([] | [_] | [_; _] | _ :: _ :: _ :: _ :: _) ) -> diff --git a/middle_end/flambda2/terms/flambda_primitive.ml b/middle_end/flambda2/terms/flambda_primitive.ml index ceb00ff74aa..548841c1277 100644 --- a/middle_end/flambda2/terms/flambda_primitive.ml +++ b/middle_end/flambda2/terms/flambda_primitive.ml @@ -94,6 +94,87 @@ module Array_kind = struct | Naked_floats -> Flambda_kind.With_subkind.naked_float end +module Array_ref_kind = struct + type t = + | Immediates + | Values + | Naked_floats of Alloc_mode.For_allocations.t + + let [@ocamlformat "disable"] print ppf t = + match t with + | Immediates -> Format.pp_print_string ppf "Immediates" + | Values -> Format.pp_print_string ppf "Values" + | Naked_floats mode -> + Format.fprintf ppf "@[(Naked_floats %a)@]" + Alloc_mode.For_allocations.print mode + + let compare = Stdlib.compare + + let element_kind_for_set t = + match t with + | Immediates | Values -> K.value + | Naked_floats _ -> K.naked_float + + let element_kind_for_creation = element_kind_for_set + + let element_kind_for_load = element_kind_for_set + + let to_lambda t : Lambda.array_ref_kind = + match t with + | Immediates -> Pintarray_ref + | Values -> Paddrarray_ref + | Naked_floats mode -> + Pfloatarray_ref (match mode with + | Heap -> Lambda.alloc_heap + | Local _ -> Lambda.alloc_local) + + let element_kind t = + match t with + | Immediates -> Flambda_kind.With_subkind.tagged_immediate + | Values -> Flambda_kind.With_subkind.any_value + | Naked_floats _ -> Flambda_kind.With_subkind.naked_float +end + +module Array_set_kind = struct + type t = + | Immediates + | Values of Alloc_mode.For_assignments.t + | Naked_floats + + let [@ocamlformat "disable"] print ppf t = + match t with + | Immediates -> Format.pp_print_string ppf "Immediates" + | Values mode -> + Format.fprintf ppf "@[(Values %a)@]" + Alloc_mode.For_assignments.print mode + | Naked_floats -> Format.fprintf ppf "Naked_floats" + + let compare = Stdlib.compare + + let element_kind_for_set t = + match t with + | Immediates | Values _ -> K.value + | Naked_floats -> K.naked_float + + let element_kind_for_creation = element_kind_for_set + + let element_kind_for_load = element_kind_for_set + + let to_lambda t : Lambda.array_set_kind = + match t with + | Immediates -> Pintarray_set + | Values mode -> Paddrarray_set (match mode with + | Heap -> Lambda.modify_heap + | Local -> Lambda.modify_maybe_stack) + | Naked_floats -> Pfloatarray_set + + let element_kind t = + match t with + | Immediates -> Flambda_kind.With_subkind.tagged_immediate + | Values _ -> Flambda_kind.With_subkind.any_value + | Naked_floats -> Flambda_kind.With_subkind.naked_float +end + module Duplicate_block_kind = struct type t = | Values of @@ -1321,7 +1402,7 @@ let ids_for_export_binary_primitive p = type ternary_primitive = | Block_set of Block_access_kind.t * Init_or_assign.t - | Array_set of Array_kind.t * Init_or_assign.t + | Array_set of Array_set_kind.t * Init_or_assign.t | Bytes_or_bigstring_set of bytes_like_value * string_accessor_width | Bigarray_set of num_dimensions * Bigarray_kind.t * Bigarray_layout.t @@ -1372,8 +1453,8 @@ let print_ternary_primitive ppf p = fprintf ppf "(Block_set %a %a)" Block_access_kind.print kind Init_or_assign.print init | Array_set (kind, init) -> - fprintf ppf "(Array_set %a %a)" Array_kind.print kind Init_or_assign.print - init + fprintf ppf "(Array_set %a %a)" + Array_set_kind.print kind Init_or_assign.print init | Bytes_or_bigstring_set (kind, string_accessor_width) -> fprintf ppf "(Bytes_set %a %a)" print_bytes_like_value kind print_string_accessor_width string_accessor_width @@ -1389,7 +1470,7 @@ let args_kind_of_ternary_primitive p = block_index_kind, Block_access_kind.element_kind_for_set access_kind ) | Array_set (kind, _) -> - array_kind, array_index_kind, Array_kind.element_kind_for_set kind + array_kind, array_index_kind, Array_set_kind.element_kind_for_set kind | Bytes_or_bigstring_set (Bytes, (Eight | Sixteen)) -> string_or_bytes_kind, bytes_or_bigstring_index_kind, K.naked_immediate | Bytes_or_bigstring_set (Bytes, Thirty_two) -> diff --git a/middle_end/flambda2/terms/flambda_primitive.mli b/middle_end/flambda2/terms/flambda_primitive.mli index 172d655afbd..1826efee9ea 100644 --- a/middle_end/flambda2/terms/flambda_primitive.mli +++ b/middle_end/flambda2/terms/flambda_primitive.mli @@ -53,6 +53,44 @@ module Array_kind : sig val element_kind : t -> Flambda_kind.With_subkind.t end +module Array_ref_kind : sig + type t = + | Immediates (** An array consisting only of immediate values. *) + | Values + (** An array consisting of elements of kind [value]. With the float + array optimisation enabled, such elements must never be [float]s. *) + | Naked_floats of Alloc_mode.For_allocations.t + (** An array consisting of naked floats, represented using + [Double_array_tag]. *) + + val print : Format.formatter -> t -> unit + + val compare : t -> t -> int + + val to_lambda : t -> Lambda.array_ref_kind + + val element_kind : t -> Flambda_kind.With_subkind.t +end + +module Array_set_kind : sig + type t = + | Immediates (** An array consisting only of immediate values. *) + | Values of Alloc_mode.For_assignments.t + (** An array consisting of elements of kind [value]. With the float + array optimisation enabled, such elements must never be [float]s. *) + | Naked_floats + (** An array consisting of naked floats, represented using + [Double_array_tag]. *) + + val print : Format.formatter -> t -> unit + + val compare : t -> t -> int + + val to_lambda : t -> Lambda.array_set_kind + + val element_kind : t -> Flambda_kind.With_subkind.t +end + module Duplicate_block_kind : sig type t = | Values of @@ -338,6 +376,8 @@ type binary_float_arith_op = type binary_primitive = | Block_load of Block_access_kind.t * Mutability.t | Array_load of Array_kind.t * Mutability.t + (** Doesn't need [Array_ref_kind.t] because it doesn't box any returned + [float]s. *) | String_or_bigstring_load of string_like_value * string_accessor_width | Bigarray_load of num_dimensions * Bigarray_kind.t * Bigarray_layout.t | Phys_equal of equality_comparison @@ -352,7 +392,7 @@ type binary_primitive = (** Primitives taking exactly three arguments. *) type ternary_primitive = | Block_set of Block_access_kind.t * Init_or_assign.t - | Array_set of Array_kind.t * Init_or_assign.t + | Array_set of Array_set_kind.t * Init_or_assign.t | Bytes_or_bigstring_set of bytes_like_value * string_accessor_width | Bigarray_set of num_dimensions * Bigarray_kind.t * Bigarray_layout.t From 6b1f0069c2530a5b83bab74aadfef5d2f06256cf Mon Sep 17 00:00:00 2001 From: Mark Shinwell Date: Thu, 1 Jun 2023 16:57:23 +0100 Subject: [PATCH 5/8] Finish flambda2 wiring + add caml_modify_local_prototype in memory.h --- .../flambda2/from_lambda/lambda_to_flambda.ml | 12 +-- .../lambda_to_flambda_primitives.ml | 86 ++++++++-------- .../flambda2/parser/fexpr_to_flambda.ml | 9 +- .../flambda2/parser/flambda_to_fexpr.ml | 14 ++- .../simplify/simplify_ternary_primitive.ml | 37 +++++-- middle_end/flambda2/terms/code_size.ml | 13 ++- .../flambda2/terms/flambda_primitive.ml | 98 ++++++------------- .../flambda2/terms/flambda_primitive.mli | 35 ++----- .../flambda2/to_cmm/to_cmm_primitive.ml | 11 +-- ocaml/runtime/caml/memory.h | 1 + 10 files changed, 144 insertions(+), 172 deletions(-) diff --git a/middle_end/flambda2/from_lambda/lambda_to_flambda.ml b/middle_end/flambda2/from_lambda/lambda_to_flambda.ml index d42babe85ad..9e6082ce03c 100644 --- a/middle_end/flambda2/from_lambda/lambda_to_flambda.ml +++ b/middle_end/flambda2/from_lambda/lambda_to_flambda.ml @@ -993,8 +993,8 @@ let primitive_result_kind (prim : Lambda.primitive) : | Pccall { prim_native_repr_res = _, Unboxed_float; _ } | Pfloatofint _ | Pnegfloat _ | Pabsfloat _ | Paddfloat _ | Psubfloat _ | Pmulfloat _ | Pdivfloat _ | Pfloatfield _ - | Parrayrefs Pfloatarray - | Parrayrefu Pfloatarray + | Parrayrefs (Pfloatarray_ref _) + | Parrayrefu (Pfloatarray_ref _) | Pbigarrayref (_, _, (Pbigarray_float32 | Pbigarray_float64), _) -> Flambda_kind.With_subkind.boxed_float | Pccall { prim_native_repr_res = _, Unboxed_integer Pnativeint; _ } @@ -1019,8 +1019,8 @@ let primitive_result_kind (prim : Lambda.primitive) : | Pstringlength | Pstringrefu | Pbyteslength | Pbytesrefu | Pbytessetu | Parraylength _ | Parraysetu _ | Pisint _ | Pbintcomp _ | Pintofbint _ | Pisout - | Parrayrefs Pintarray - | Parrayrefu Pintarray + | Parrayrefs Pintarray_ref + | Parrayrefu Pintarray_ref | Pprobe_is_enabled _ | Pctconst _ | Pbswap16 | Pbigarrayref ( _, @@ -1055,8 +1055,8 @@ let primitive_result_kind (prim : Lambda.primitive) : (* CR ncourant: this should be bottom, but we don't have it *) Flambda_kind.With_subkind.any_value | Pccall { prim_native_repr_res = _, Same_as_ocaml_repr; _ } - | Parrayrefs (Pgenarray | Paddrarray) - | Parrayrefu (Pgenarray | Paddrarray) + | Parrayrefs (Pgenarray_ref _ | Paddrarray_ref) + | Parrayrefu (Pgenarray_ref _ | Paddrarray_ref) | Pbytes_to_string | Pbytes_of_string | Parray_of_iarray | Parray_to_iarray | Pgetglobal _ | Psetglobal _ | Pgetpredef _ | Pmakeblock _ | Pmakefloatblock _ | Pfield _ | Pfield_computed _ | Pduprecord _ diff --git a/middle_end/flambda2/from_lambda/lambda_to_flambda_primitives.ml b/middle_end/flambda2/from_lambda/lambda_to_flambda_primitives.ml index 848937af7bb..0e139d359b7 100644 --- a/middle_end/flambda2/from_lambda/lambda_to_flambda_primitives.ml +++ b/middle_end/flambda2/from_lambda/lambda_to_flambda_primitives.ml @@ -131,34 +131,40 @@ let convert_array_kind (kind : L.array_kind) : converted_array_kind = | Pintarray -> Array_kind Immediates | Pfloatarray -> Array_kind Naked_floats +module Array_ref_kind = struct + type t = + | Immediates + | Values + | Naked_floats of L.alloc_mode +end + type converted_array_ref_kind = - | Array_ref_kind of P.Array_ref_kind.t - | Float_array_opt_dynamic_ref of Alloc_mode.For_allocations.t + | Array_ref_kind of Array_ref_kind.t + | Float_array_opt_dynamic_ref of L.alloc_mode -let convert_array_ref_kind - ~current_region (kind : L.array_ref_kind) : converted_array_ref_kind = +let convert_array_ref_kind (kind : L.array_ref_kind) : converted_array_ref_kind + = match kind with | Pgenarray_ref mode -> check_float_array_optimisation_enabled (); - Float_array_opt_dynamic_ref - (Alloc_mode.For_allocations.from_lambda ~current_region mode) + Float_array_opt_dynamic_ref mode | Paddrarray_ref -> Array_ref_kind Values | Pintarray_ref -> Array_ref_kind Immediates - | Pfloatarray_ref mode -> - Array_ref_kind - (Naked_floats (Alloc_mode.For_allocations.from_lambda ~current_region mode)) + | Pfloatarray_ref mode -> Array_ref_kind (Naked_floats mode) type converted_array_set_kind = | Array_set_kind of P.Array_set_kind.t | Float_array_opt_dynamic_set of Alloc_mode.For_assignments.t -let convert_array_set_kind (kind : L.array_set_kind) : converted_array_set_kind = +let convert_array_set_kind (kind : L.array_set_kind) : converted_array_set_kind + = match kind with | Pgenarray_set mode -> check_float_array_optimisation_enabled (); Float_array_opt_dynamic_set (Alloc_mode.For_assignments.from_lambda mode) | Paddrarray_set mode -> - Array_set_kind (Values (Alloc_mode.For_assignments.from_lambda mode)) + Array_set_kind + (Values (Assignment (Alloc_mode.For_assignments.from_lambda mode))) | Pintarray_set -> Array_set_kind Immediates | Pfloatarray_set -> Array_set_kind Naked_floats @@ -522,53 +528,39 @@ let check_array_access ~dbg ~array ~index primitive : H.expr_primitive = ~conditions:(array_access_validity_condition array index) ~dbg -let array_load_unsafe ~array ~index (array_ref_kind : P.Array_ref_kind.t) - ~current_region : H.expr_primitive = +let array_load_unsafe ~array ~index (array_ref_kind : Array_ref_kind.t) + ~current_region : H.expr_primitive = match array_ref_kind with - | Immediates | Values -> - Binary (Array_load (array_ref_kind, Mutable), array, index) + | Immediates -> Binary (Array_load (Immediates, Mutable), array, index) + | Values -> Binary (Array_load (Values, Mutable), array, index) | Naked_floats mode -> box_float mode (Binary (Array_load (Naked_floats, Mutable), array, index)) ~current_region -let array_set_unsafe ~array ~index ~new_value (array_set_kind : P.Array_set_kind.t) : - H.expr_primitive = - match array_set_kind with - | Immediates - Ternary - ( Array_set (array_kind, Assignment Alloc_mode.For_assignments.heap), - array, - index, - new_value ) - | Values -> - Ternary - ( Array_set (array_kind, Assignment Alloc_mode.For_assignments.heap), - array, - index, - new_value ) - | Naked_floats -> - Ternary - ( Array_set - (Naked_floats, Assignment (Alloc_mode.For_assignments.local ())), - array, - index, - unbox_float new_value ) +let array_set_unsafe ~array ~index ~new_value + (array_set_kind : P.Array_set_kind.t) : H.expr_primitive = + let new_value = + match array_set_kind with + | Immediates | Values _ -> new_value + | Naked_floats -> unbox_float new_value + in + Ternary (Array_set array_set_kind, array, index, new_value) -let[@inline always] match_on_array_ref_kind ~current_region ~array array_ref_kind f - : H.expr_primitive = - match convert_array_ref_kind ~current_region array_ref_kind with +let[@inline always] match_on_array_ref_kind ~array array_ref_kind f : + H.expr_primitive = + match convert_array_ref_kind array_ref_kind with | Array_ref_kind array_ref_kind -> f array_ref_kind | Float_array_opt_dynamic_ref mode -> (* CR keryan: we should push the ITE as low as possible to avoid duplicating too much *) If_then_else ( Unary (Is_flat_float_array, array), - f (P.Array_ref_kind.Naked_floats mode), - f P.Array_ref_kind.Values ) + f (Array_ref_kind.Naked_floats mode), + f Array_ref_kind.Values ) -let[@inline always] match_on_array_set_kind ~array array_ref_kind f - : H.expr_primitive = +let[@inline always] match_on_array_set_kind ~array array_ref_kind f : + H.expr_primitive = match convert_array_set_kind array_ref_kind with | Array_set_kind array_set_kind -> f array_set_kind | Float_array_opt_dynamic_set mode -> @@ -577,7 +569,7 @@ let[@inline always] match_on_array_set_kind ~array array_ref_kind f If_then_else ( Unary (Is_flat_float_array, array), f P.Array_set_kind.Naked_floats, - f (P.Array_set_kind.Values mode) ) + f (P.Array_set_kind.Values (Assignment mode)) ) (* Safe arith (div/mod by zero) *) let checked_arith_op ~dbg (bi : Lambda.boxed_integer option) op mode arg1 arg2 @@ -1022,11 +1014,11 @@ let convert_lprim ~big_endian (prim : L.primitive) (args : Simple.t list) (* For this and the following cases we will end up relying on the backend to CSE the two accesses to the array's header word in the [Pgenarray] case. *) - match_on_array_ref_kind ~current_region ~array array_ref_kind + match_on_array_ref_kind ~array array_ref_kind (array_load_unsafe ~array ~index ~current_region) | Parrayrefs array_ref_kind, [array; index] -> check_array_access ~dbg ~array ~index - (match_on_array_ref_kind ~current_region ~array array_ref_kind + (match_on_array_ref_kind ~array array_ref_kind (array_load_unsafe ~array ~index ~current_region)) | Parraysetu array_set_kind, [array; index; new_value] -> match_on_array_set_kind ~array array_set_kind diff --git a/middle_end/flambda2/parser/fexpr_to_flambda.ml b/middle_end/flambda2/parser/fexpr_to_flambda.ml index b2d59f96a11..fcdabe32758 100644 --- a/middle_end/flambda2/parser/fexpr_to_flambda.ml +++ b/middle_end/flambda2/parser/fexpr_to_flambda.ml @@ -441,7 +441,14 @@ let binop (binop : Fexpr.binop) : Flambda_primitive.binary_primitive = let ternop env (ternop : Fexpr.ternop) : Flambda_primitive.ternary_primitive = match ternop with - | Array_set (ak, ia) -> Array_set (ak, init_or_assign env ia) + | Array_set (ak, ia) -> + let ask : Flambda_primitive.Array_set_kind.t = + match ak, ia with + | Immediates, _ -> Immediates + | Naked_floats, _ -> Naked_floats + | Values, ia -> Values (init_or_assign env ia) + in + Array_set ask | Block_set (bk, ia) -> Block_set (block_access_kind bk, init_or_assign env ia) | Bytes_or_bigstring_set (blv, saw) -> Bytes_or_bigstring_set (blv, saw) diff --git a/middle_end/flambda2/parser/flambda_to_fexpr.ml b/middle_end/flambda2/parser/flambda_to_fexpr.ml index e0473c1d8e4..844ae908008 100644 --- a/middle_end/flambda2/parser/flambda_to_fexpr.ml +++ b/middle_end/flambda2/parser/flambda_to_fexpr.ml @@ -582,7 +582,19 @@ let binop (op : Flambda_primitive.binary_primitive) : Fexpr.binop = let ternop env (op : Flambda_primitive.ternary_primitive) : Fexpr.ternop = match op with - | Array_set (ak, ia) -> Array_set (ak, init_or_assign env ia) + | Array_set ak -> + let ia : Flambda_primitive.Init_or_assign.t = + match ak with + | Values ia -> ia + | Immediates | Naked_floats -> Assignment Alloc_mode.For_assignments.heap + in + let ak : Flambda_primitive.Array_kind.t = + match ak with + | Immediates -> Immediates + | Values _ -> Values + | Naked_floats -> Naked_floats + in + Array_set (ak, init_or_assign env ia) | Block_set (bk, ia) -> Block_set (block_access_kind bk, init_or_assign env ia) | Bytes_or_bigstring_set (blv, saw) -> Bytes_or_bigstring_set (blv, saw) | Bigarray_set _ -> diff --git a/middle_end/flambda2/simplify/simplify_ternary_primitive.ml b/middle_end/flambda2/simplify/simplify_ternary_primitive.ml index 21b90c13176..1cc75aba7d1 100644 --- a/middle_end/flambda2/simplify/simplify_ternary_primitive.ml +++ b/middle_end/flambda2/simplify/simplify_ternary_primitive.ml @@ -16,12 +16,16 @@ open! Simplify_import -let simplify_array_set (array_kind : P.Array_kind.t) init_or_assign dacc - ~original_term:_ dbg ~arg1:array ~arg1_ty:array_ty ~arg2:index ~arg2_ty:_ - ~arg3:new_value ~arg3_ty:_ ~result_var = - let elt_kind = P.Array_kind.element_kind array_kind |> K.With_subkind.kind in +let simplify_array_set (array_set_kind : P.Array_set_kind.t) dacc ~original_term + dbg ~arg1:array ~arg1_ty:array_ty ~arg2:index ~arg2_ty:_ ~arg3:new_value + ~arg3_ty:_ ~result_var = + let elt_kind = + P.Array_set_kind.element_kind array_set_kind |> K.With_subkind.kind + in let array_kind = - Simplify_common.specialise_array_kind dacc array_kind ~array_ty + Simplify_common.specialise_array_kind dacc + (P.Array_set_kind.array_kind array_set_kind) + ~array_ty in match array_kind with | Bottom -> SPR.create_invalid dacc @@ -30,10 +34,26 @@ let simplify_array_set (array_kind : P.Array_kind.t) init_or_assign dacc P.Array_kind.element_kind array_kind |> K.With_subkind.kind in assert (K.equal elt_kind elt_kind'); + let array_set_kind : P.Array_set_kind.t = + match array_kind with + | Immediates -> Immediates + | Values -> ( + match array_set_kind with + | Values init_or_assign -> Values init_or_assign + | Immediates + (* We don't expect specialisation regressions from Immediates to + Values. *) + | Naked_floats -> + Misc.fatal_errorf + "Didn't expect array specialisation to yield array kind %a from \ + array set kind %a:@ %a" + P.Array_kind.print array_kind P.Array_set_kind.print array_set_kind + Named.print original_term) + | Naked_floats -> Naked_floats + in let named = Named.create_prim - (Ternary - (Array_set (array_kind, init_or_assign), array, index, new_value)) + (Ternary (Array_set array_set_kind, array, index, new_value)) dbg in let unit_ty = Flambda2_types.this_tagged_immediate Targetint_31_63.zero in @@ -59,8 +79,7 @@ let simplify_ternary_primitive dacc original_prim (prim : P.ternary_primitive) let original_term = Named.create_prim original_prim dbg in let simplifier = match prim with - | Array_set (array_kind, init_or_assign) -> - simplify_array_set array_kind init_or_assign + | Array_set array_kind -> simplify_array_set array_kind | Block_set (block_access_kind, init_or_assign) -> simplify_block_set block_access_kind init_or_assign | Bytes_or_bigstring_set (bytes_like_value, string_accessor_width) -> diff --git a/middle_end/flambda2/terms/code_size.ml b/middle_end/flambda2/terms/code_size.ml index c8263e5b635..4c3e8eb612a 100644 --- a/middle_end/flambda2/terms/code_size.ml +++ b/middle_end/flambda2/terms/code_size.ml @@ -148,12 +148,11 @@ let block_set (kind : Flambda_primitive.Block_access_kind.t) | Values _, (Assignment Local | Initialization) -> 1 (* cadda + store *) | Naked_floats _, (Assignment _ | Initialization) -> 1 -let array_set (kind : Flambda_primitive.Array_kind.t) - (init : Flambda_primitive.Init_or_assign.t) = - match kind, init with - | Values, Assignment Heap -> nonalloc_extcall_size - | Values, (Assignment Local | Initialization) -> 1 - | (Immediates | Naked_floats), (Assignment _ | Initialization) -> 1 +let array_set (kind : Flambda_primitive.Array_set_kind.t) = + match kind with + | Values (Assignment Heap) -> nonalloc_extcall_size + | Values (Assignment Local | Initialization) -> 1 + | Immediates | Naked_floats -> 1 let string_or_bigstring_load kind width = let start_address_load = @@ -360,7 +359,7 @@ let binary_prim_size prim = let ternary_prim_size prim = match (prim : Flambda_primitive.ternary_primitive) with | Block_set (block_access, init) -> block_set block_access init - | Array_set (kind, init) -> array_set kind init + | Array_set kind -> array_set kind | Bytes_or_bigstring_set (kind, width) -> bytes_like_set kind width | Bigarray_set (_dims, (Complex32 | Complex64), _layout) -> 5 (* ~ 3 block_load + 2 block_set *) diff --git a/middle_end/flambda2/terms/flambda_primitive.ml b/middle_end/flambda2/terms/flambda_primitive.ml index 548841c1277..ad3e23904e7 100644 --- a/middle_end/flambda2/terms/flambda_primitive.ml +++ b/middle_end/flambda2/terms/flambda_primitive.ml @@ -94,59 +94,38 @@ module Array_kind = struct | Naked_floats -> Flambda_kind.With_subkind.naked_float end -module Array_ref_kind = struct +module Init_or_assign = struct type t = - | Immediates - | Values - | Naked_floats of Alloc_mode.For_allocations.t + | Initialization + | Assignment of Alloc_mode.For_assignments.t let [@ocamlformat "disable"] print ppf t = + let fprintf = Format.fprintf in match t with - | Immediates -> Format.pp_print_string ppf "Immediates" - | Values -> Format.pp_print_string ppf "Values" - | Naked_floats mode -> - Format.fprintf ppf "@[(Naked_floats %a)@]" - Alloc_mode.For_allocations.print mode + | Initialization -> fprintf ppf "Init" + | Assignment Heap -> fprintf ppf "Assign Heap" + | Assignment Local -> fprintf ppf "Assign Local" let compare = Stdlib.compare - let element_kind_for_set t = - match t with - | Immediates | Values -> K.value - | Naked_floats _ -> K.naked_float - - let element_kind_for_creation = element_kind_for_set - - let element_kind_for_load = element_kind_for_set - - let to_lambda t : Lambda.array_ref_kind = - match t with - | Immediates -> Pintarray_ref - | Values -> Paddrarray_ref - | Naked_floats mode -> - Pfloatarray_ref (match mode with - | Heap -> Lambda.alloc_heap - | Local _ -> Lambda.alloc_local) - - let element_kind t = + let to_lambda t : Lambda.initialization_or_assignment = match t with - | Immediates -> Flambda_kind.With_subkind.tagged_immediate - | Values -> Flambda_kind.With_subkind.any_value - | Naked_floats _ -> Flambda_kind.With_subkind.naked_float + | Initialization -> Heap_initialization + | Assignment mode -> Assignment (Alloc_mode.For_assignments.to_lambda mode) end module Array_set_kind = struct type t = | Immediates - | Values of Alloc_mode.For_assignments.t + | Values of Init_or_assign.t | Naked_floats - let [@ocamlformat "disable"] print ppf t = + let print ppf t = match t with | Immediates -> Format.pp_print_string ppf "Immediates" - | Values mode -> - Format.fprintf ppf "@[(Values %a)@]" - Alloc_mode.For_assignments.print mode + | Values init_or_assign -> + Format.fprintf ppf "@[(Values %a)@]" Init_or_assign.print + init_or_assign | Naked_floats -> Format.fprintf ppf "Naked_floats" let compare = Stdlib.compare @@ -156,14 +135,21 @@ module Array_set_kind = struct | Immediates | Values _ -> K.value | Naked_floats -> K.naked_float - let element_kind_for_creation = element_kind_for_set - - let element_kind_for_load = element_kind_for_set + let array_kind t : Array_kind.t = + match t with + | Immediates -> Immediates + | Values _ -> Values + | Naked_floats -> Naked_floats let to_lambda t : Lambda.array_set_kind = match t with | Immediates -> Pintarray_set - | Values mode -> Paddrarray_set (match mode with + | Values Initialization -> + (* CR mshinwell: this loses the Initialization information *) + Paddrarray_set Lambda.modify_heap + | Values (Assignment mode) -> + Paddrarray_set + (match mode with | Heap -> Lambda.modify_heap | Local -> Lambda.modify_maybe_stack) | Naked_floats -> Pfloatarray_set @@ -308,26 +294,6 @@ type string_or_bytes = | String | Bytes -module Init_or_assign = struct - type t = - | Initialization - | Assignment of Alloc_mode.For_assignments.t - - let [@ocamlformat "disable"] print ppf t = - let fprintf = Format.fprintf in - match t with - | Initialization -> fprintf ppf "Init" - | Assignment Heap -> fprintf ppf "Assign Heap" - | Assignment Local -> fprintf ppf "Assign Local" - - let compare = Stdlib.compare - - let to_lambda t : Lambda.initialization_or_assignment = - match t with - | Initialization -> Heap_initialization - | Assignment mode -> Assignment (Alloc_mode.For_assignments.to_lambda mode) -end - type array_like_operation = | Reading | Writing @@ -1402,7 +1368,7 @@ let ids_for_export_binary_primitive p = type ternary_primitive = | Block_set of Block_access_kind.t * Init_or_assign.t - | Array_set of Array_set_kind.t * Init_or_assign.t + | Array_set of Array_set_kind.t | Bytes_or_bigstring_set of bytes_like_value * string_accessor_width | Bigarray_set of num_dimensions * Bigarray_kind.t * Bigarray_layout.t @@ -1423,9 +1389,7 @@ let compare_ternary_primitive p1 p2 = | Block_set (kind1, init_or_assign1), Block_set (kind2, init_or_assign2) -> let c = Block_access_kind.compare kind1 kind2 in if c <> 0 then c else Init_or_assign.compare init_or_assign1 init_or_assign2 - | Array_set (kind1, init_or_assign1), Array_set (kind2, init_or_assign2) -> - let c = Array_kind.compare kind1 kind2 in - if c <> 0 then c else Init_or_assign.compare init_or_assign1 init_or_assign2 + | Array_set kind1, Array_set kind2 -> Array_kind.compare kind1 kind2 | ( Bytes_or_bigstring_set (kind1, width1), Bytes_or_bigstring_set (kind2, width2) ) -> let c = Stdlib.compare kind1 kind2 in @@ -1452,9 +1416,7 @@ let print_ternary_primitive ppf p = | Block_set (kind, init) -> fprintf ppf "(Block_set %a %a)" Block_access_kind.print kind Init_or_assign.print init - | Array_set (kind, init) -> - fprintf ppf "(Array_set %a %a)" - Array_set_kind.print kind Init_or_assign.print init + | Array_set kind -> fprintf ppf "(Array_set %a)" Array_set_kind.print kind | Bytes_or_bigstring_set (kind, string_accessor_width) -> fprintf ppf "(Bytes_set %a %a)" print_bytes_like_value kind print_string_accessor_width string_accessor_width @@ -1469,7 +1431,7 @@ let args_kind_of_ternary_primitive p = ( block_kind, block_index_kind, Block_access_kind.element_kind_for_set access_kind ) - | Array_set (kind, _) -> + | Array_set kind -> array_kind, array_index_kind, Array_set_kind.element_kind_for_set kind | Bytes_or_bigstring_set (Bytes, (Eight | Sixteen)) -> string_or_bytes_kind, bytes_or_bigstring_index_kind, K.naked_immediate diff --git a/middle_end/flambda2/terms/flambda_primitive.mli b/middle_end/flambda2/terms/flambda_primitive.mli index 1826efee9ea..b83c2209ba9 100644 --- a/middle_end/flambda2/terms/flambda_primitive.mli +++ b/middle_end/flambda2/terms/flambda_primitive.mli @@ -53,29 +53,18 @@ module Array_kind : sig val element_kind : t -> Flambda_kind.With_subkind.t end -module Array_ref_kind : sig +module Init_or_assign : sig type t = - | Immediates (** An array consisting only of immediate values. *) - | Values - (** An array consisting of elements of kind [value]. With the float - array optimisation enabled, such elements must never be [float]s. *) - | Naked_floats of Alloc_mode.For_allocations.t - (** An array consisting of naked floats, represented using - [Double_array_tag]. *) - - val print : Format.formatter -> t -> unit - - val compare : t -> t -> int - - val to_lambda : t -> Lambda.array_ref_kind + | Initialization + | Assignment of Alloc_mode.For_assignments.t - val element_kind : t -> Flambda_kind.With_subkind.t + val to_lambda : t -> Lambda.initialization_or_assignment end module Array_set_kind : sig type t = | Immediates (** An array consisting only of immediate values. *) - | Values of Alloc_mode.For_assignments.t + | Values of Init_or_assign.t (** An array consisting of elements of kind [value]. With the float array optimisation enabled, such elements must never be [float]s. *) | Naked_floats @@ -88,6 +77,8 @@ module Array_set_kind : sig val to_lambda : t -> Lambda.array_set_kind + val array_kind : t -> Array_kind.t + val element_kind : t -> Flambda_kind.With_subkind.t end @@ -154,14 +145,6 @@ type string_or_bytes = | String | Bytes -module Init_or_assign : sig - type t = - | Initialization - | Assignment of Alloc_mode.For_assignments.t - - val to_lambda : t -> Lambda.initialization_or_assignment -end - type 'signed_or_unsigned comparison = | Eq | Neq @@ -376,8 +359,6 @@ type binary_float_arith_op = type binary_primitive = | Block_load of Block_access_kind.t * Mutability.t | Array_load of Array_kind.t * Mutability.t - (** Doesn't need [Array_ref_kind.t] because it doesn't box any returned - [float]s. *) | String_or_bigstring_load of string_like_value * string_accessor_width | Bigarray_load of num_dimensions * Bigarray_kind.t * Bigarray_layout.t | Phys_equal of equality_comparison @@ -392,7 +373,7 @@ type binary_primitive = (** Primitives taking exactly three arguments. *) type ternary_primitive = | Block_set of Block_access_kind.t * Init_or_assign.t - | Array_set of Array_set_kind.t * Init_or_assign.t + | Array_set of Array_set_kind.t | Bytes_or_bigstring_set of bytes_like_value * string_accessor_width | Bigarray_set of num_dimensions * Bigarray_kind.t * Bigarray_layout.t diff --git a/middle_end/flambda2/to_cmm/to_cmm_primitive.ml b/middle_end/flambda2/to_cmm/to_cmm_primitive.ml index fc2809e90bf..705e1f45616 100644 --- a/middle_end/flambda2/to_cmm/to_cmm_primitive.ml +++ b/middle_end/flambda2/to_cmm/to_cmm_primitive.ml @@ -138,16 +138,15 @@ let array_load ~dbg (kind : P.Array_kind.t) ~arr ~index = let addr_array_store init ~arr ~index ~new_value dbg = match (init : P.Init_or_assign.t) with - | Assignment Heap -> C.addr_array_set arr index new_value dbg + | Assignment Heap -> C.addr_array_set_heap arr index new_value dbg | Assignment Local -> C.addr_array_set_local arr index new_value dbg | Initialization -> C.addr_array_initialize arr index new_value dbg -let array_set ~dbg (kind : P.Array_kind.t) (init : P.Init_or_assign.t) ~arr - ~index ~new_value = +let array_set ~dbg (kind : P.Array_set_kind.t) ~arr ~index ~new_value = let expr = match kind with | Immediates -> C.int_array_set arr index new_value dbg - | Values -> addr_array_store init ~arr ~index ~new_value dbg + | Values init -> addr_array_store init ~arr ~index ~new_value dbg | Naked_floats -> C.float_array_set arr index new_value dbg in C.return_unit dbg expr @@ -657,8 +656,8 @@ let ternary_primitive _env dbg f x y z = match (f : P.ternary_primitive) with | Block_set (block_access, init) -> block_set ~dbg block_access init ~block:x ~index:y ~new_value:z - | Array_set (array_kind, init) -> - array_set ~dbg array_kind init ~arr:x ~index:y ~new_value:z + | Array_set array_set_kind -> + array_set ~dbg array_set_kind ~arr:x ~index:y ~new_value:z | Bytes_or_bigstring_set (kind, width) -> bytes_or_bigstring_set ~dbg kind width ~bytes:x ~index:y ~new_value:z | Bigarray_set (_dimensions, kind, _layout) -> diff --git a/ocaml/runtime/caml/memory.h b/ocaml/runtime/caml/memory.h index 911dd566b05..800b768fa78 100644 --- a/ocaml/runtime/caml/memory.h +++ b/ocaml/runtime/caml/memory.h @@ -55,6 +55,7 @@ CAMLextern void caml_adjust_gc_speed (mlsize_t, mlsize_t); CAMLextern void caml_alloc_dependent_memory (mlsize_t bsz); CAMLextern void caml_free_dependent_memory (mlsize_t bsz); CAMLextern void caml_modify (value *, value); +CAMLextern void caml_modify_local (value obj, intnat i, value val); CAMLextern void caml_initialize (value *, value); CAMLextern value caml_check_urgent_gc (value); CAMLextern color_t caml_allocation_color (void *hp); From 39ef4f828221ec349c879c2a4ba3595b01640df6 Mon Sep 17 00:00:00 2001 From: Antal Spector-Zabusky Date: Thu, 1 Jun 2023 15:53:03 -0400 Subject: [PATCH 6/8] `make fmt` --- backend/cmm_helpers.mli | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/cmm_helpers.mli b/backend/cmm_helpers.mli index f763ec8a1e3..e45c3d848ff 100644 --- a/backend/cmm_helpers.mli +++ b/backend/cmm_helpers.mli @@ -341,7 +341,11 @@ val addr_array_initialize : expression -> expression -> expression -> Debuginfo.t -> expression val addr_array_set : - Lambda.modify_mode -> expression -> expression -> expression -> Debuginfo.t -> + Lambda.modify_mode -> + expression -> + expression -> + expression -> + Debuginfo.t -> expression val int_array_set : From 35311c4ff61f68d610143156222d7d37f67bffdf Mon Sep 17 00:00:00 2001 From: Antal Spector-Zabusky Date: Mon, 12 Jun 2023 12:45:33 -0400 Subject: [PATCH 7/8] Bootstrap --- ocaml/boot/ocamlc | Bin 3483429 -> 3483361 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ocaml/boot/ocamlc b/ocaml/boot/ocamlc index 7535aa61de30a4b316c8ffbdde88c84a75482587..658409231c056a4477eb76ea577eebf1ba9dc562 100755 GIT binary patch delta 553 zcmZ2_?h50>vsV}!S{PfHT9{i{T3B1yTG(4SS~y#{TDV(yT6kOdTKHQ8S_E5!T7+9f zT0~pKTEtrPL5_tv1{NeRK8OYZXCUTcnEtO*Tx@!QsUpktGxro^rVIR&lbD{s%qTE@ z!6FfX=>j>r9Mc^xaURoQ^&kKL&ozBw8j~>7-$&EivNf~> z(;xo-&j~bD8)Q5XxK2Nqts%}RH$5;&OL+PMH3gpbAK4l}tO>+gK&%bKIzX%o#Ckxi e55xvQYzV|gKy1AIN4ClGT}<8_(-*3lIsgFLg_$=1 From 8f2a549817afd8d65974d0bd89800db2cc664db0 Mon Sep 17 00:00:00 2001 From: Antal Spector-Zabusky Date: Tue, 13 Jun 2023 15:24:01 -0400 Subject: [PATCH 8/8] Add tests of immutable array literals containing variables --- .../tests/lib-array/iarrays_with_variables.ml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 ocaml/testsuite/tests/lib-array/iarrays_with_variables.ml diff --git a/ocaml/testsuite/tests/lib-array/iarrays_with_variables.ml b/ocaml/testsuite/tests/lib-array/iarrays_with_variables.ml new file mode 100644 index 00000000000..60188ec4afe --- /dev/null +++ b/ocaml/testsuite/tests/lib-array/iarrays_with_variables.ml @@ -0,0 +1,58 @@ +(* TEST + flags = "-extension immutable_arrays" + * expect +*) + +let one = Sys.opaque_identity 1;; +let two = Sys.opaque_identity 2;; +let three = Sys.opaque_identity 3;; + +let phi = Sys.opaque_identity 1.618;; +let e = Sys.opaque_identity 2.718281828459045;; +let pi = Sys.opaque_identity 3.14159265358;; + +let alpha = Sys.opaque_identity "alpha";; +let beta = Sys.opaque_identity "beta";; +let gamma = Sys.opaque_identity "gamma";; + +[%%expect{| +val one : int = 1 +val two : int = 2 +val three : int = 3 +val phi : float = 1.618 +val e : float = 2.71828182845904509 +val pi : float = 3.14159265358 +val alpha : string = "alpha" +val beta : string = "beta" +val gamma : string = "gamma" +|}];; + +[:one; two; three:];; +[%%expect{| +- : int iarray = [:1; 2; 3:] +|}];; + +[:0; one; two; three; 4:];; +[%%expect{| +- : int iarray = [:0; 1; 2; 3; 4:] +|}];; + +[:phi; e; pi:];; +[%%expect{| +- : float iarray = [:1.618; 2.71828182845904509; 3.14159265358:] +|}];; + +[:1.414; phi; e; pi; 6.28:];; +[%%expect{| +- : float iarray = [:1.414; 1.618; 2.71828182845904509; 3.14159265358; 6.28:] +|}];; + +[:alpha; beta; gamma:];; +[%%expect{| +- : string iarray = [:"alpha"; "beta"; "gamma":] +|}];; + +[:"Greek:"; alpha; beta; gamma; "delta":];; +[%%expect{| +- : string iarray = [:"Greek:"; "alpha"; "beta"; "gamma"; "delta":] +|}];;