Skip to content

Commit 15d352f

Browse files
committed
Compiler: recognize and optimize String.concat
1 parent a28514e commit 15d352f

13 files changed

+244
-60
lines changed

compiler/lib/generate.ml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,16 @@ let rec translate_expr ctx queue loc x e level : _ * J.statement_list =
12781278
let (py, cy), queue = access_queue' ~ctx queue b in
12791279
let prop = or_p px py in
12801280
bool (J.EBin (J.EqEq, cx, cy)), prop, queue
1281+
| Extern "caml_string_concat", [ a; b ] when Config.Flag.use_js_string () ->
1282+
let (pa, ca), queue = access_queue' ~ctx queue a in
1283+
let (pb, cb), queue = access_queue' ~ctx queue b in
1284+
let prop = or_p pa pb in
1285+
let rec add ca cb =
1286+
match cb with
1287+
| J.EBin (J.Plus, cb1, cb2) -> J.EBin (J.Plus, add ca cb1, cb2)
1288+
| _ -> J.EBin (J.Plus, ca, cb)
1289+
in
1290+
add ca cb, prop, queue
12811291
| Extern name, l -> (
12821292
let name = Primitive.resolve name in
12831293
match internal_prim name with

compiler/lib/specialize_js.ml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,48 @@ let specialize_instr info i =
147147
| _ -> i)
148148
| _ -> i
149149

150+
let equal2 a b = Code.Var.equal a b
151+
152+
let equal3 a b c = Code.Var.equal a b && Code.Var.equal b c
153+
154+
let equal4 a b c d = Code.Var.equal a b && Code.Var.equal b c && Code.Var.equal c d
155+
150156
let specialize_instrs info l =
151157
let rec aux info checks l acc =
152158
match l with
153159
| [] -> List.rev acc
160+
| [ ((Let (alen, Prim (Extern "caml_ml_string_length", [ Pv a ])), _) as len1)
161+
; ((Let (blen, Prim (Extern "caml_ml_string_length", [ Pv b ])), _) as len2)
162+
; ((Let (len, Prim (Extern "%int_add", [ Pv alen'; Pv blen' ])), _) as len3)
163+
; (Let (bytes, Prim (Extern "caml_create_bytes", [ Pv len' ])), _)
164+
; ( Let
165+
( u1
166+
, Prim
167+
( Extern "caml_blit_string"
168+
, [ Pv a'; Pc (Int 0l); Pv bytes'; Pc (Int 0l); Pv alen'' ] ) )
169+
, _ )
170+
; ( Let
171+
( u2
172+
, Prim
173+
( Extern "caml_blit_string"
174+
, [ Pv b'; Pc (Int 0l); Pv bytes''; Pv alen'''; Pv blen'' ] ) )
175+
, _ )
176+
; (Let (res, Prim (Extern "caml_string_of_bytes", [ Pv bytes''' ])), _)
177+
]
178+
when equal2 a a'
179+
&& equal2 b b'
180+
&& equal2 len len'
181+
&& equal4 alen alen' alen'' alen'''
182+
&& equal3 blen blen' blen''
183+
&& equal4 bytes bytes' bytes'' bytes''' ->
184+
[ len1
185+
; len2
186+
; len3
187+
; Let (u1, Constant (Int 0l)), No
188+
; Let (u2, Constant (Int 0l)), No
189+
; Let (res, Prim (Extern "caml_string_concat", [ Pv a; Pv b ])), No
190+
; Let (bytes, Prim (Extern "caml_bytes_of_string", [ Pv res ])), No
191+
]
154192
| (i, loc) :: r -> (
155193
(* We make bound checking explicit. Then, we can remove duplicated
156194
bound checks. Also, it appears to be more efficient to inline

compiler/lib/var_printer.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ let name t v nm_orig =
114114
match str, nm_orig with
115115
| "", ">>=" -> "symbol_bind"
116116
| "", ">>|" -> "symbol_map"
117+
| "", "^" -> "symbol_concat"
117118
| "", _ -> "symbol"
118119
| str, _ -> str
119120
in

compiler/tests-check-prim/main.output

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ caml_array_of_bytes
157157
caml_array_of_string
158158
caml_bytes_of_utf16_jsstring
159159
caml_new_string
160+
caml_string_concat
160161
caml_string_set16
161162
caml_string_set32
162163
caml_string_set64

compiler/tests-check-prim/main.output5

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ From +mlBytes.js:
138138
caml_array_of_bytes
139139
caml_array_of_string
140140
caml_bytes_of_utf16_jsstring
141+
caml_string_concat
141142
caml_string_set16
142143
caml_string_set32
143144
caml_string_set64

compiler/tests-check-prim/unix-unix.output

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ caml_array_of_bytes
266266
caml_array_of_string
267267
caml_bytes_of_utf16_jsstring
268268
caml_new_string
269+
caml_string_concat
269270
caml_string_set16
270271
caml_string_set32
271272
caml_string_set64

compiler/tests-check-prim/unix-unix.output5

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ From +mlBytes.js:
247247
caml_array_of_bytes
248248
caml_array_of_string
249249
caml_bytes_of_utf16_jsstring
250+
caml_string_concat
250251
caml_string_set16
251252
caml_string_set32
252253
caml_string_set64

compiler/tests-check-prim/unix-win32.output

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ From +mlBytes.js:
230230
caml_array_of_bytes
231231
caml_array_of_string
232232
caml_bytes_of_utf16_jsstring
233+
caml_string_concat
233234
caml_new_string
234235
caml_string_set16
235236
caml_string_set32

compiler/tests-check-prim/unix-win32.output5

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ From +mlBytes.js:
213213
caml_array_of_bytes
214214
caml_array_of_string
215215
caml_bytes_of_utf16_jsstring
216+
caml_string_concat
216217
caml_string_set16
217218
caml_string_set32
218219
caml_string_set64

compiler/tests-compiler/dune.inc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,21 @@
749749
(preprocess
750750
(pps ppx_expect)))
751751

752+
(library
753+
;; compiler/tests-compiler/test_string.ml
754+
(name test_string_15)
755+
(enabled_if true)
756+
(modules test_string)
757+
(libraries js_of_ocaml_compiler unix str jsoo_compiler_expect_tests_helper)
758+
(inline_tests
759+
(enabled_if true)
760+
(deps
761+
(file %{project_root}/compiler/bin-js_of_ocaml/js_of_ocaml.exe)
762+
(file %{project_root}/compiler/bin-jsoo_minify/jsoo_minify.exe)))
763+
(flags (:standard -open Jsoo_compiler_expect_tests_helper))
764+
(preprocess
765+
(pps ppx_expect)))
766+
752767
(library
753768
;; compiler/tests-compiler/unix_fs.ml
754769
(name unix_fs_15)
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
(* Js_of_ocaml tests
2+
* http://www.ocsigen.org/js_of_ocaml/
3+
* Copyright (C) 2019 Ty Overby
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation; either version 2 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18+
*)
19+
20+
open Util
21+
22+
let%expect_test _ =
23+
let program =
24+
compile_and_parse
25+
~debug:false
26+
~use_js_string:true
27+
{|
28+
external string_length : string -> int = "%string_length"
29+
external bytes_create : int -> bytes = "caml_create_bytes"
30+
external string_blit : string -> int -> bytes -> int -> int -> unit
31+
= "caml_blit_string" [@@noalloc]
32+
external bytes_unsafe_to_string : bytes -> string = "%bytes_to_string"
33+
34+
let ( ^ ) s1 s2 =
35+
let l1 = string_length s1 and l2 = string_length s2 in
36+
let s = bytes_create (l1 + l2) in
37+
string_blit s1 0 s 0 l1;
38+
string_blit s2 0 s l1 l2;
39+
bytes_unsafe_to_string s
40+
41+
let here () =
42+
let a = "a" in
43+
let b = "b" in
44+
a ^ a ^ b ^ b
45+
46+
let (_ : string) = here ()
47+
|}
48+
in
49+
print_program program;
50+
[%expect
51+
{|
52+
(function(globalThis){
53+
"use strict";
54+
var
55+
runtime = globalThis.jsoo_runtime,
56+
cst_a = "a",
57+
cst_b = "b",
58+
caml_string_concat = runtime.caml_string_concat;
59+
function _a_(_b_){return cst_a + cst_a + cst_b + cst_b;}
60+
_a_(0);
61+
var Test = [0, caml_string_concat, _a_];
62+
runtime.caml_register_global(2, Test, "Test");
63+
return;
64+
}
65+
(globalThis));
66+
//end |}]
67+
68+
let%expect_test _ =
69+
let program =
70+
compile_and_parse
71+
~debug:false
72+
~use_js_string:false
73+
{|
74+
external string_length : string -> int = "%string_length"
75+
external bytes_create : int -> bytes = "caml_create_bytes"
76+
external string_blit : string -> int -> bytes -> int -> int -> unit
77+
= "caml_blit_string" [@@noalloc]
78+
79+
external bytes_unsafe_to_string : bytes -> string = "%bytes_to_string"
80+
81+
let ( ^ ) s1 s2 =
82+
let l1 = string_length s1 and l2 = string_length s2 in
83+
let s = bytes_create (l1 + l2) in
84+
string_blit s1 0 s 0 l1;
85+
string_blit s2 0 s l1 l2;
86+
bytes_unsafe_to_string s
87+
88+
let here () =
89+
let a = "a" in
90+
let b = "b" in
91+
a ^ a ^ b ^ b
92+
93+
let (_ : string) = here ()
94+
|}
95+
in
96+
print_program program;
97+
[%expect
98+
{|
99+
(function(globalThis){
100+
"use strict";
101+
var
102+
runtime = globalThis.jsoo_runtime,
103+
caml_string_concat = runtime.caml_string_concat,
104+
caml_string_of_jsbytes = runtime.caml_string_of_jsbytes,
105+
cst_a = caml_string_of_jsbytes("a"),
106+
cst_b = caml_string_of_jsbytes("b");
107+
function _a_(_b_){
108+
return caml_string_concat
109+
(cst_a,
110+
caml_string_concat(cst_a, caml_string_concat(cst_b, cst_b)));
111+
}
112+
_a_(0);
113+
var Test = [0, caml_string_concat, _a_];
114+
runtime.caml_register_global(2, Test, "Test");
115+
return;
116+
}
117+
(globalThis));
118+
//end |}]

0 commit comments

Comments
 (0)