Skip to content

Commit 879efc0

Browse files
galpeteryichoi
authored andcommitted
Merge snapshots to reduce code size (jerryscript-project#1266)
As of fe26674 JerryScript can merge multiple snapshots into one and execute them by index. By merging snapshots the code size (rodata) is reduced by ~10 kb on arm/x86_64. IoT.js-DCO-1.0-Signed-off-by: Peter Gal pgal.u-szeged@partner.samsung.com
1 parent 1900829 commit 879efc0

File tree

8 files changed

+119
-42
lines changed

8 files changed

+119
-42
lines changed

cmake/iotjs.cmake

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
9595
endif()
9696

9797
if(ENABLE_SNAPSHOT)
98-
set(JS2C_SNAPSHOT_ARG --snapshot-generator=${JERRY_HOST})
98+
set(JS2C_SNAPSHOT_ARG --snapshot-generator=${JERRY_HOST}
99+
--snapshot-merger=${JERRY_HOST}-snapshot)
99100
set(IOTJS_CFLAGS ${IOTJS_CFLAGS} -DENABLE_SNAPSHOT)
100101
endif()
101102

cmake/jerry.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ ExternalProject_Add(hostjerry
2727
-DJERRY_LIBC=OFF
2828
-DJERRY_CMDLINE=ON
2929
-DJERRY_CMDLINE_MINIMAL=OFF
30+
-DJERRY_CMDLINE_SNAPSHOT=ON
3031
-DFEATURE_SNAPSHOT_SAVE=${ENABLE_SNAPSHOT}
3132
-DFEATURE_PROFILE=es5.1
3233
)

deps/jerry

Submodule jerry updated 61 files

src/iotjs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ static bool iotjs_run(iotjs_environment_t* env) {
100100
iotjs_jval_t jmain = iotjs_jhelper_eval("iotjs.js", strlen("iotjs.js"),
101101
iotjs_s, iotjs_l, false, &throws);
102102
#else
103-
iotjs_jval_t jmain = iotjs_jhelper_exec_snapshot(iotjs_s, iotjs_l, &throws);
103+
iotjs_jval_t jmain = iotjs_exec_snapshot(module_iotjs_idx, &throws);
104104
#endif
105105

106106
if (throws) {

src/iotjs_binding.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "iotjs_def.h"
1818
#include "iotjs_binding.h"
19+
#include "iotjs_js.h"
1920

2021
#include <string.h>
2122

@@ -446,17 +447,16 @@ iotjs_jval_t iotjs_jhelper_eval(const char* name, size_t name_len,
446447

447448

448449
#ifdef ENABLE_SNAPSHOT
449-
iotjs_jval_t iotjs_jhelper_exec_snapshot(const void* snapshot_p,
450-
size_t snapshot_size, bool* throws) {
451-
jerry_value_t res = jerry_exec_snapshot(snapshot_p, snapshot_size, false);
450+
iotjs_jval_t iotjs_exec_snapshot(uint32_t snapshot_function_idx, bool* throws) {
451+
/* iotjs_js_modules_{s,l} is generated by the js2c.py */
452+
jerry_value_t result =
453+
jerry_exec_snapshot_at((const void*)iotjs_js_modules_s,
454+
iotjs_js_modules_l, snapshot_function_idx, false);
452455
/* the snapshot buffer can be referenced
453456
* until jerry_cleanup is not called */
454-
455-
*throws = jerry_value_has_error_flag(res);
456-
457-
jerry_value_clear_error_flag(&res);
458-
459-
return iotjs_jval_create_raw(res);
457+
*throws = jerry_value_has_error_flag(result);
458+
jerry_value_clear_error_flag(&result);
459+
return result;
460460
}
461461
#endif
462462

src/iotjs_binding.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,7 @@ iotjs_jval_t iotjs_jhelper_eval(const char* name, size_t name_len,
173173
const uint8_t* data, size_t size,
174174
bool strict_mode, bool* throws);
175175
#ifdef ENABLE_SNAPSHOT
176-
// Evaluates javascript snapshot.
177-
iotjs_jval_t iotjs_jhelper_exec_snapshot(const void* snapshot_p,
178-
size_t snapshot_size, bool* throws);
176+
iotjs_jval_t iotjs_exec_snapshot(uint32_t snapshot_function_idx, bool* throws);
179177
#endif
180178

181179

src/modules/iotjs_module_process.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,7 @@ JHANDLER_FUNCTION(CompileNativePtr) {
130130
if (natives[i].name != NULL) {
131131
bool throws;
132132
#ifdef ENABLE_SNAPSHOT
133-
iotjs_jval_t jres = iotjs_jhelper_exec_snapshot(natives[i].code,
134-
natives[i].length, &throws);
133+
jerry_value_t jres = iotjs_exec_snapshot(natives[i].idx, &throws);
135134
#else
136135
iotjs_jval_t jres =
137136
WrapEval(name, iotjs_string_size(&id), (const char*)natives[i].code,

tools/js2c.py

Lines changed: 103 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,26 @@ def force_str(string):
5555

5656

5757
def parse_literals(code):
58-
JERRY_SNAPSHOT_VERSION = 7
58+
JERRY_SNAPSHOT_VERSION = 8
59+
JERRY_SNAPSHOT_MAGIC = 0x5952524A
5960

6061
literals = set()
61-
62-
header = struct.unpack('IIII', code[0:16])
63-
if header[0] != JERRY_SNAPSHOT_VERSION :
62+
# header format:
63+
# uint32_t magic
64+
# uint32_t version
65+
# uint32_t global opts
66+
# uint32_t literal table offset
67+
header = struct.unpack('I' * 4, code[0:4 * 4])
68+
if header[0] != JERRY_SNAPSHOT_MAGIC:
69+
print('Incorrect snapshot format! Magic number is incorrect')
70+
exit(1)
71+
if header[1] != JERRY_SNAPSHOT_VERSION:
6472
print ('Please check jerry snapshot version (Last confirmed: %d)'
6573
% JERRY_SNAPSHOT_VERSION)
6674
exit(1)
6775

68-
code_ptr = header[1] + 8
76+
code_ptr = header[3] + 4
77+
6978
while code_ptr < len(code):
7079
length = struct.unpack('H', code[code_ptr : code_ptr + 2])[0]
7180
code_ptr = code_ptr + 2
@@ -117,6 +126,25 @@ def parse_literals(code):
117126

118127
MAGIC_STRINGS_HEADER = '#define JERRY_MAGIC_STRING_ITEMS \\\n'
119128

129+
MODULE_SNAPSHOT_VARIABLES_H = '''
130+
extern const char module_{NAME}[];
131+
extern const uint32_t module_{NAME}_idx;
132+
'''
133+
134+
MODULE_SNAPSHOT_VARIABLES_C = '''
135+
#define MODULE_{NAME}_IDX ({IDX})
136+
const char module_{NAME}[] = "{NAME}";
137+
const uint32_t module_{NAME}_idx = MODULE_{NAME}_IDX;
138+
'''
139+
140+
NATIVE_SNAPSHOT_STRUCT_H = '''
141+
typedef struct {
142+
const char* name;
143+
const uint32_t idx;
144+
} iotjs_js_module;
145+
146+
extern const iotjs_js_module natives[];
147+
'''
120148

121149
MODULE_VARIABLES_H = '''
122150
extern const char {NAME}_n[];
@@ -168,6 +196,28 @@ def format_code(code, indent):
168196
return "\n".join(lines)
169197

170198

199+
def merge_snapshots(snapshot_infos, snapshot_merger):
200+
output_path = fs.join(path.SRC_ROOT, 'js','merged.modules')
201+
cmd = [snapshot_merger, "merge", "-o", output_path]
202+
cmd.extend([item['path'] for item in snapshot_infos])
203+
204+
ret = subprocess.call(cmd)
205+
206+
if ret != 0:
207+
msg = "Failed to merge %s: - %d" % (snapshot_infos, ret)
208+
print("%s%s%s" % ("\033[1;31m", msg, "\033[0m"))
209+
exit(1)
210+
211+
for item in snapshot_infos:
212+
fs.remove(item['path'])
213+
214+
with open(output_path, 'rb') as snapshot:
215+
code = snapshot.read()
216+
217+
fs.remove(output_path)
218+
return code
219+
220+
171221
def get_snapshot_contents(module_name, snapshot_generator):
172222
""" Convert the given module with the snapshot generator
173223
and return the resulting bytes.
@@ -189,18 +239,15 @@ def get_snapshot_contents(module_name, snapshot_generator):
189239
"--save-snapshot-for-eval",
190240
snapshot_path,
191241
wrapped_path])
242+
243+
fs.remove(wrapped_path)
192244
if ret != 0:
193245
msg = "Failed to dump %s: - %d" % (js_path, ret)
194246
print("%s%s%s" % ("\033[1;31m", msg, "\033[0m"))
247+
fs.remove(snapshot_path)
195248
exit(1)
196249

197-
with open(snapshot_path, 'rb') as snapshot:
198-
code = snapshot.read()
199-
200-
fs.remove(wrapped_path)
201-
fs.remove(snapshot_path)
202-
203-
return code
250+
return snapshot_path
204251

205252

206253
def get_js_contents(name, is_debug_mode=False):
@@ -216,7 +263,8 @@ def get_js_contents(name, is_debug_mode=False):
216263
return code
217264

218265

219-
def js2c(buildtype, no_snapshot, js_modules, js_dumper, verbose=False):
266+
def js2c(buildtype, no_snapshot, js_modules, js_dumper, snapshot_merger,
267+
verbose=False):
220268
is_debug_mode = buildtype == "debug"
221269
magic_string_set = set()
222270

@@ -236,33 +284,61 @@ def js2c(buildtype, no_snapshot, js_modules, js_dumper, verbose=False):
236284
fout_c.write(LICENSE)
237285
fout_c.write(HEADER2)
238286

239-
for name in sorted(js_modules):
287+
snapshot_infos = []
288+
for idx, name in enumerate(sorted(js_modules)):
240289
if verbose:
241290
print('Processing module: %s' % name)
242291

243292
if no_snapshot:
244293
code = get_js_contents(name, is_debug_mode)
294+
code_string = format_code(code, 1)
295+
296+
fout_h.write(MODULE_VARIABLES_H.format(NAME=name))
297+
fout_c.write(MODULE_VARIABLES_C.format(NAME=name,
298+
NAME_UPPER=name.upper(),
299+
SIZE=len(code),
300+
CODE=code_string))
245301
else:
246-
code = get_snapshot_contents(name, js_dumper)
247-
magic_string_set |= parse_literals(code)
302+
code_path = get_snapshot_contents(name, js_dumper)
303+
info = {'name': name, 'path': code_path, 'idx': idx}
304+
snapshot_infos.append(info)
305+
306+
fout_h.write(MODULE_SNAPSHOT_VARIABLES_H.format(NAME=name))
307+
fout_c.write(MODULE_SNAPSHOT_VARIABLES_C.format(NAME=name,
308+
IDX=idx))
248309

310+
311+
if no_snapshot:
312+
modules_struct = [
313+
' {{ {0}_n, {0}_s, SIZE_{1} }},'.format(name, name.upper())
314+
for name in sorted(js_modules)
315+
]
316+
modules_struct.append(' { NULL, NULL, 0 }')
317+
else:
318+
code = merge_snapshots(snapshot_infos, snapshot_merger)
249319
code_string = format_code(code, 1)
320+
magic_string_set |= parse_literals(code)
250321

322+
name = 'iotjs_js_modules'
251323
fout_h.write(MODULE_VARIABLES_H.format(NAME=name))
252324
fout_c.write(MODULE_VARIABLES_C.format(NAME=name,
253325
NAME_UPPER=name.upper(),
254326
SIZE=len(code),
255327
CODE=code_string))
328+
modules_struct = [
329+
' {{ module_{0}, MODULE_{0}_IDX }},'.format(info['name'])
330+
for info in snapshot_infos
331+
]
332+
modules_struct.append(' { NULL, 0 }')
333+
334+
if no_snapshot:
335+
native_struct_h = NATIVE_STRUCT_H
336+
else:
337+
native_struct_h = NATIVE_SNAPSHOT_STRUCT_H
256338

257-
fout_h.write(NATIVE_STRUCT_H)
339+
fout_h.write(native_struct_h)
258340
fout_h.write(FOOTER1)
259341

260-
modules_struct = [
261-
' {{ {0}_n, {0}_s, SIZE_{1} }},'.format(name, name.upper())
262-
for name in sorted(js_modules)
263-
]
264-
modules_struct.append(' { NULL, NULL, 0 }')
265-
266342
fout_c.write(NATIVE_STRUCT_C.format(MODULES="\n".join(modules_struct)))
267343
fout_c.write(EMPTY_LINE)
268344

@@ -294,12 +370,14 @@ def js2c(buildtype, no_snapshot, js_modules, js_dumper, verbose=False):
294370
parser.add_argument('--snapshot-generator', default=None,
295371
help='Executable to use for generating snapshots from the JS files. '
296372
'If not specified the JS files will be directly processed.')
373+
parser.add_argument('--snapshot-merger', default=None,
374+
help='Executable to use to merge snapshots.')
297375
parser.add_argument('-v', '--verbose', default=False,
298376
help='Enable verbose output.')
299377

300378
options = parser.parse_args()
301379

302-
if not options.snapshot_generator:
380+
if not options.snapshot_generator or not options.snapshot_merger:
303381
print('Converting JS modules to C arrays (no snapshot)')
304382
no_snapshot = True
305383
else:
@@ -308,4 +386,4 @@ def js2c(buildtype, no_snapshot, js_modules, js_dumper, verbose=False):
308386

309387
modules = options.modules.replace(',', ' ').split()
310388
js2c(options.buildtype, no_snapshot, modules, options.snapshot_generator,
311-
options.verbose)
389+
options.snapshot_merger, options.verbose)

0 commit comments

Comments
 (0)