Skip to content

Commit eb8cdfb

Browse files
committed
[Tolk] Peephole optimization: PUSHINT+STU to STSLICECONST
Now, `b.storeInt(123, 32)` generates not `123 PUSHINT; SWAP; STI`, but `x{...} STSLICECONST`. Although, for certain conditions, still generate STUR/STUR targeting smaller bytecode.
1 parent 55c2530 commit eb8cdfb

File tree

7 files changed

+53
-45
lines changed

7 files changed

+53
-45
lines changed

tolk-tester/tests/cells-slices.tolk

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -473,26 +473,23 @@ We test that consequtive storeInt/storeUint with constants are joined into a sin
473473
@fif_codegen
474474
"""
475475
test6() PROC:<{
476-
18446744082299486211 PUSHINT
477476
NEWC
478-
96 STU // '2
477+
x{000000010000000200000003} STSLICECONST
479478
ENDC // '11
480479
"""
481480

482481
@fif_codegen
483482
"""
484483
test17() PROC:<{
485-
4219 PUSHINT
486484
NEWC
487-
16 STU
485+
x{107b} STSLICECONST
488486
"""
489487

490488
@fif_codegen
491489
"""
492490
test18() PROC:<{
493-
421 PUSHINT
494491
NEWC
495-
26 STU
492+
b{00000000000000000110100101} STSLICECONST
496493
"""
497494

498495
@fif_codegen
@@ -501,8 +498,7 @@ We test that consequtive storeInt/storeUint with constants are joined into a sin
501498
123 PUSHINT
502499
NEWC
503500
4 STI
504-
16 PUSHPOW2DEC
505-
16 STUR
501+
x{ffff} STSLICECONST
506502
-1 PUSHINT
507503
8 STIR
508504
}>
@@ -511,9 +507,8 @@ We test that consequtive storeInt/storeUint with constants are joined into a sin
511507
@fif_codegen
512508
"""
513509
test20() PROC:<{
514-
40976 PUSHINT
515510
NEWC
516-
16 STU
511+
x{a010} STSLICECONST
517512
"""
518513

519514
@fif_codegen
@@ -529,12 +524,10 @@ We test that consequtive storeInt/storeUint with constants are joined into a sin
529524
test22() PROC:<{
530525
NEWC // '2
531526
NEWC // b1 b2
532-
2056 PUSHINT
533-
ROT
534-
24 STU // b2 b1
527+
SWAP // b2 b1
528+
x{000808} STSLICECONST // b2 b1
535529
SWAP // b1 b2
536-
10141514286835656557042350424064 PUSHINTX
537-
132 STUR // b1 b2
530+
x{000000080010000000000000000000000} STSLICECONST // b1 b2
538531
"""
539532

540533
@fif_codegen
@@ -543,13 +536,11 @@ We test that consequtive storeInt/storeUint with constants are joined into a sin
543536
NEWC
544537
SWAP
545538
IF:<{
546-
91343852333181432387730302044911803916571639814 PUSHINT
547-
256 STUR
539+
x{0000000000000000000000001000000000000000000000000200000000000006} STSLICECONST
548540
8 PUSHINT
549541
256 STUR
550542
}>ELSE<{
551-
56539106072908298546665520023773392506479484700019806659963456035401760775 PUSHINT
552-
255 STIR
543+
b{000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000111} STSLICECONST
553544
}>
554545
BBITS
555546
}>
@@ -597,9 +588,8 @@ We test that consequtive storeInt/storeUint with constants are joined into a sin
597588
s3 POP
598589
42 EQINT
599590
IF:<{
600-
0 PUSHINT
601-
ROT
602-
3 STU
591+
SWAP
592+
b{000} STSLICECONST
603593
8 STU // b
604594
}>ELSE<{
605595
DROP // b

tolk-tester/tests/constants-tests.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,5 +126,5 @@ fun main() {
126126
@testcase | 105 | | -1 0 7 48
127127
@testcase | 106 | | -1 0 0 -1
128128

129-
@code_hash 39394999917297360167950120811797040756088634052063904555277102239104815314650
129+
@code_hash 32362412747322136329528616455651783746542516198110452861733590440068294458753
130130
*/

tolk-tester/tests/inline-tests.tolk

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -467,13 +467,11 @@ fun usedIn10ButDeclaredBelow(x: int) {
467467
@fif_codegen
468468
"""
469469
test8() PROC:<{ //
470-
5 PUSHINT
471470
NEWC
472-
32 STU // '3
471+
x{00000005} STSLICECONST // '3
473472
25 PUSHINT // '3 '9
474-
107374182425 PUSHINT
475-
ROT
476-
64 STU // x '3
473+
SWAP // x self
474+
x{0000001900000019} STSLICECONST // x '3
477475
SWAP // '3 x
478476
}>
479477
"""
@@ -509,9 +507,8 @@ fun usedIn10ButDeclaredBelow(x: int) {
509507
test10() PROC:<{
510508
5 PUSHINT // '3=5
511509
DUP // '3=5 y=5
512-
5 PUSHINT
513510
NEWC
514-
32 STU // a=5 b=5 c
511+
x{00000005} STSLICECONST // a=5 b=5 c
515512
BBITS // a=5 b=5 '18
516513
}>
517514
"""

tolk-tester/tests/lazy-load-tests.tolk

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,9 +1900,8 @@ fun main() {
19001900
x{0102} PUSHSLICE
19011901
8 LDI
19021902
NIP
1903-
15 PUSHINT
19041903
NEWC
1905-
8 STI
1904+
x{0f} STSLICECONST
19061905
STSLICE
19071906
"""
19081907

@@ -1917,9 +1916,8 @@ fun main() {
19171916
320 PUSHINT // lazyS '14=320
19181917
PLDSLICEX // '15
19191918
PUSHNULL // '15 st.extensions
1920-
0 PUSHINT
19211919
NEWC
1922-
1 STU // '15 st.extensions b
1920+
b{0} STSLICECONST // '15 st.extensions b
19231921
s1 s2 XCHG // st.extensions '15 b
19241922
STSLICE // st.extensions b
19251923
STOPTREF // b

tolk-tester/tests/pack-unpack-1.tolk

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,8 @@ fun main(c: cell) {
107107
@fif_codegen
108108
"""
109109
test3() PROC:<{
110-
42949672980 PUSHINT
111110
NEWC
112-
64 STI // b
111+
x{0000000a00000014} STSLICECONST // b
113112
ENDC // c
114113
CTOS // s
115114
64 LDU

tolk-tester/tests/strings-tests.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,5 @@ fun test1() {
7878
@testcase | 0 | | 0
7979
@testcase | 101 | | [ 65 66 67 68 ]
8080

81-
@code_hash 33596158548631231081906594921712427436857221384530839008514203474433205250299
81+
@code_hash 52991558142486159683575375539594116045551936710713942466413832119934760926753
8282
*/

tolk/optimize.cpp

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ bool Optimizer::detect_rewrite_big_THROW() {
164164
return true;
165165
}
166166

167-
// purpose 1: for one constant b.storeInt(123, 32) generate not "123 PUSHINT; SWAP; STI", but "123 PUSHINT; STIR"
168-
// purpose 2: consecutive b.storeUint(ff, 16).storeUint(ff, 16) generate one "00ff00ff" STU
167+
// purpose 1: for b.storeInt(123, 32) generate not "123 PUSHINT; SWAP; STI", but "x{...} STSLICECONST"
168+
// purpose 2: consecutive b.storeUint(ff, 16).storeUint(ff, 16) generate one "x{00ff00ff} STSLICECONST"
169169
// (since it works at IR level, it also works for const variables and auto-serialization)
170170
bool Optimizer::detect_rewrite_MY_store_int() {
171171
bool first_my_store = op_[0]->is_custom() && op_[0]->op.starts_with("MY_store_int");
@@ -198,14 +198,38 @@ bool Optimizer::detect_rewrite_MY_store_int() {
198198
n_merged++;
199199
}
200200

201-
p_ = n_merged;
202-
q_ = 2;
203-
oq_[0] = std::make_unique<AsmOp>(AsmOp::IntConst(op_[0]->loc, total_number));
204-
if (total_number == 0 && total_len == 4 && first_unsigned) { // "STGRAMS" stores four 0-bits cheaper than "4 STUR"
205-
oq_[1] = std::make_unique<AsmOp>(AsmOp::Custom(op_[0]->loc, "STGRAMS", 1, 1));
206-
} else {
201+
// we do not want to always use STSLICECONST; for example, storing "0" 64-bit via x{00...} is more effective
202+
// for a single operation, but in practice, total bytecode becomes larger, which has a cumulative negative effect;
203+
// here is a heuristic "when to use STSLICECONST, when leave PUSHINT + STUR", based on real contracts measurements
204+
bool use_stsliceconst = total_len <= 32 || (total_len <= 48 && total_number >= 256) || (total_len <= 64 && total_number >= 65536)
205+
|| (total_len <= 96 && total_number >= (1ULL<<32)) || (total_number > (1ULL<<62));
206+
if (!use_stsliceconst) {
207+
p_ = n_merged;
208+
q_ = 2;
209+
oq_[0] = std::make_unique<AsmOp>(AsmOp::IntConst(op_[0]->loc, total_number));
207210
oq_[1] = std::make_unique<AsmOp>(AsmOp::Custom(op_[0]->loc, std::to_string(total_len) + (first_unsigned ? " STUR" : " STIR"), 1, 1));
211+
return true;
208212
}
213+
214+
p_ = n_merged;
215+
q_ = 1;
216+
217+
// output "x{...}" or "b{...}" (if length not divisible by 4)
218+
const td::RefInt256 base = td::make_refint(total_len % 4 == 0 ? 16 : 2);
219+
const int s_len = base == 16 ? total_len / 4 : total_len;
220+
const char* digits = "0123456789abcdef";
221+
222+
std::string result(s_len + 3, '0');
223+
result[0] = base == 16 ? 'x' : 'b';
224+
result[1] = '{';
225+
result[s_len + 3 - 1] = '}';
226+
for (int i = s_len - 1; i >= 0 && total_number != 0; --i) {
227+
result[2 + i] = digits[(total_number % base)->to_long()];
228+
total_number /= base;
229+
}
230+
231+
result += " STSLICECONST";
232+
oq_[0] = std::make_unique<AsmOp>(AsmOp::Custom(op_[0]->loc, result, 0, 1));
209233
return true;
210234
}
211235

0 commit comments

Comments
 (0)