From 5e1bed735266d65bcc6b31e9f69e21694ecc2674 Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Mon, 28 Sep 2026 23:08:11 +0200 Subject: [PATCH 1/3] feat: add more peephole optimisations Add optimisations for increments and decrements of a variable deeper in the stack (e.g. x = x + 1 inside a branch), scope cleanup of deeper items, stack shuffles that OP_TUCK, OP_3DUP or OP_2SWAP express shorter, and OP_SIZE on a duplicated value. The increment and decrement rules come before the deeper drop rules, which would otherwise match the end of their patterns first. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../bytes_type_narrowing.ts | 16 +-- .../valid-contract-files/complex_loop.ts | 16 +-- .../deep_increment_decrement.ts | 78 +++++++++++++ .../valid-contract-files/deep_replace.ts | 12 +- .../do_while_loop_no_introspection.ts | 12 +- .../valid-contract-files/double_negation.ts | 16 +-- .../valid-contract-files/everything.ts | 26 ++--- .../for_loop_stack_items.ts | 16 +-- .../valid-contract-files/for_while_nested.ts | 20 ++-- .../global_function_altstack_cleanup.ts | 16 +-- .../multiline_statements.ts | 14 +-- .../valid-contract-files/split_size.ts | 12 +- .../tuple_reassignment.ts | 18 +-- .../tuple_reassignment_after_final_read.ts | 12 +- .../tuple_reassignment_narrowing.ts | 14 +-- .../valid-contract-files/while_loop_nested.ts | 16 +-- .../deep_increment_decrement.cash | 27 +++++ packages/utils/src/optimisations.ts | 52 +++++++++ .../test/fixtures/bitauth-script.fixture.ts | 104 +++++++++--------- website/docs/releases/release-notes.md | 1 + 20 files changed, 328 insertions(+), 170 deletions(-) create mode 100644 packages/cashc/test/generation/fixtures/valid-contract-files/deep_increment_decrement.ts create mode 100644 packages/cashc/test/valid-contract-files/deep_increment_decrement.cash diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/bytes_type_narrowing.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/bytes_type_narrowing.ts index 6d54d128a..058467516 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/bytes_type_narrowing.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/bytes_type_narrowing.ts @@ -103,7 +103,7 @@ export const fixtures: Fixture[] = [ // function ifNarrowingMultiple(bytes data1, bytes data2, int x) { + 'OP_5 OP_NUMEQUALVERIFY ' // if (data1.length == 20 && data2.length == 10) { - + 'OP_DUP OP_SIZE OP_NIP 14 OP_NUMEQUAL OP_2 OP_PICK OP_SIZE OP_NIP OP_10 OP_NUMEQUAL OP_BOOLAND OP_IF ' + + 'OP_SIZE 14 OP_NUMEQUAL OP_2 OP_PICK OP_SIZE OP_NIP OP_10 OP_NUMEQUAL OP_BOOLAND OP_IF ' // bytes20 narrowed1 = data1; + 'OP_DUP ' // bytes10 narrowed2 = data2; @@ -122,10 +122,10 @@ export const fixtures: Fixture[] = [ + 'OP_NIP OP_NIP ' // } + 'OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF', - fingerprint: '6ce346c2b481330bafc5f23000655b8f033cd71635e01987a431885433b9de30', + fingerprint: '39a2f3c82a5c3f2d55a80bfe84f6288d3406f7c0afd6501807a38000d9f2da13', debug: { - bytecode: '76009c6378827701149d7852798802a9147b7e01877e82770114a0776776519c6378827701149c63787653798875687b00a077776776529c630114527982779d787b87776776539c6378827701149c537900a09a63787653798875687b00a077776776549c6378827701149c537982775a9c9a697853797c537988537a8802a9147b7e01877e82770114a07767559d76827701149c527982775a9c9a63766f88765479886d687b00a077776868686868', - sourceMap: '2:4:7:5;;;;3:16:3:20;:::27:1;;:31::33:0;:8::35:1;4:27:4:31:0;5:28:5:32;;:8::34:1;6:16:6:47:0;:42::46;:16::47:1;;;:::54;;:57::59:0;:8::61:1;2:42:7:5;:4;9::15::0;;;;10:12:10:16;:::23:1;;:27::29:0;:12:::1;:31:13:9:0;11::11:35;12:20:12:28;:32::36;;:12::38:1;10:31:13:9;;14:16:14:17:0;:20::21;:8::23:1;9:44:15:5;;:4;17::21::0;;;;18:16:18:18;:22::26;;:::33:1;;:8::35;19:27:19:31:0;20:28:20:32;:8::34:1;17:44:21:5;:4;23::29::0;;;;24:12:24:16;:::23:1;;:27::29:0;:12:::1;:33::34:0;;:37::38;:33:::1;:12;:40:27:9:0;25:31:25:35;26:20:26:28;:32::36;;:12::38:1;24:40:27:9;;28:16:28:17:0;:20::21;:8::23:1;23:47:29:5;;:4;31::38::0;;;;32:16:32:21;:::28:1;;:32::34:0;:16:::1;:38::43:0;;:::50:1;;:54::56:0;:38:::1;:16;:8::58;33:28:33:33:0;34::34;;35:16:35:25;:29::34;;:8::36:1;36:29:36:34:0;;:8::36:1;37:16:37:48:0;:42::47;:16::48:1;;;:::55;;:58::60:0;:8::62:1;31:64:38:5;:4;40::48::0;;41:12:41:17;:::24:1;;:28::30:0;:12:::1;:34::39:0;;:::46:1;;:50::52:0;:34:::1;:12;:54:46:9:0;42:32:42:37;43::44:38;:::40:1;45:20:45:29:0;:33::38;;:12::40:1;41:54:46:9;;47:16:47:17:0;:20::21;:8::23:1;40:66:48:5;;1:0:49:1;;;;', + bytecode: '76009c6378827701149d7852798802a9147b7e01877e82770114a0776776519c6378827701149c63787653798875687b00a077776776529c630114527982779d787b87776776539c6378827701149c537900a09a63787653798875687b00a077776776549c6378827701149c537982775a9c9a697853797c537988537a8802a9147b7e01877e82770114a07767559d8201149c527982775a9c9a63766f88765479886d687b00a077776868686868', + sourceMap: '2:4:7:5;;;;3:16:3:20;:::27:1;;:31::33:0;:8::35:1;4:27:4:31:0;5:28:5:32;;:8::34:1;6:16:6:47:0;:42::46;:16::47:1;;;:::54;;:57::59:0;:8::61:1;2:42:7:5;:4;9::15::0;;;;10:12:10:16;:::23:1;;:27::29:0;:12:::1;:31:13:9:0;11::11:35;12:20:12:28;:32::36;;:12::38:1;10:31:13:9;;14:16:14:17:0;:20::21;:8::23:1;9:44:15:5;;:4;17::21::0;;;;18:16:18:18;:22::26;;:::33:1;;:8::35;19:27:19:31:0;20:28:20:32;:8::34:1;17:44:21:5;:4;23::29::0;;;;24:12:24:16;:::23:1;;:27::29:0;:12:::1;:33::34:0;;:37::38;:33:::1;:12;:40:27:9:0;25:31:25:35;26:20:26:28;:32::36;;:12::38:1;24:40:27:9;;28:16:28:17:0;:20::21;:8::23:1;23:47:29:5;;:4;31::38::0;;;;32:16:32:21;:::28:1;;:32::34:0;:16:::1;:38::43:0;;:::50:1;;:54::56:0;:38:::1;:16;:8::58;33:28:33:33:0;34::34;;35:16:35:25;:29::34;;:8::36:1;36:29:36:34:0;;:8::36:1;37:16:37:48:0;:42::47;:16::48:1;;;:::55;;:58::60:0;:8::62:1;31:64:38:5;:4;40::48::0;;41:12:41:24:1;:28::30:0;:12:::1;:34::39:0;;:::46:1;;:50::52:0;:34:::1;:12;:54:46:9:0;42:32:42:37;43::44:38;:::40:1;45:20:45:29:0;:33::38;;:12::40:1;41:54:46:9;;47:16:47:17:0;:20::21;:8::23:1;40:66:48:5;;1:0:49:1;;;;', logs: [], requires: [ { ip: 8, line: 3 }, @@ -141,11 +141,11 @@ export const fixtures: Fixture[] = [ { ip: 113, line: 35 }, { ip: 116, line: 36 }, { ip: 126, line: 37 }, - { ip: 145, line: 44 }, - { ip: 149, line: 45 }, - { ip: 155, line: 47 }, + { ip: 143, line: 44 }, + { ip: 147, line: 45 }, + { ip: 153, line: 47 }, ], - sourceTags: '22:22:sc;39:39:sc;44:45:sc;60:60:sc;82:82:sc;87:88:sc;126:126:sc;150:150:sc;155:156:sc', + sourceTags: '22:22:sc;39:39:sc;44:45:sc;60:60:sc;82:82:sc;87:88:sc;126:126:sc;148:148:sc;153:154:sc', }, }, }, diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/complex_loop.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/complex_loop.ts index 77f4cb841..e85dc0355 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/complex_loop.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/complex_loop.ts @@ -6,20 +6,20 @@ export const fixtures: Fixture[] = [ contractName: 'Loopy', constructorInputs: [], abi: [{ name: 'doLoop', inputs: [] }], - bytecode: 'OP_0 OP_0 OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOVALUE OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_1ADD OP_TXOUTPUTCOUNT OP_2DUP OP_LESSTHAN OP_DUP OP_IF OP_2 OP_PICK OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOT OP_NIP OP_DUP OP_IF OP_4 OP_PICK OP_3 OP_PICK OP_OUTPUTVALUE OP_ADD OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK OP_ELSE OP_3 OP_PICK OP_1ADD OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_ENDIF OP_ENDIF OP_2DROP OP_DUP OP_TXINPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_2SWAP OP_GREATERTHAN OP_VERIFY OP_SWAP OP_0 OP_GREATERTHAN OP_NIP', + bytecode: 'OP_0 OP_0 OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOVALUE OP_4 OP_ROLL OP_DROP OP_SWAP OP_2SWAP OP_ROT OP_1ADD OP_TXOUTPUTCOUNT OP_2DUP OP_LESSTHAN OP_DUP OP_IF OP_2 OP_PICK OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOT OP_NIP OP_DUP OP_IF OP_4 OP_PICK OP_3 OP_PICK OP_OUTPUTVALUE OP_ADD OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_2SWAP OP_ROT OP_FROMALTSTACK OP_ELSE OP_3 OP_ROLL OP_1ADD OP_SWAP OP_2SWAP OP_ROT OP_ENDIF OP_ENDIF OP_2DROP OP_DUP OP_TXINPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_2SWAP OP_GREATERTHAN OP_VERIFY OP_SWAP OP_0 OP_GREATERTHAN OP_NIP', debug: { - bytecode: '000000006576c6547a757c6b7b7b6c8bc46e9f76635279d100879177766354795379cc93557a757c6b7c6b7b7b6c6c6753798b547a757c6b7b7b6c68686d76c3a26672a0697c00a077', - sourceMap: '3:23:3:24;4:24:4:25;5:25:5:26;6:16:6:17;8:8:26:39;9:33:9:34;:23::41:1;:12::42;;;;;;;;10::10:22;12:20:12:37:0;13:21:13:26;::::1;15:16:15:17:0;:19:25:13;16:31:16:32;;:20::47:1;:51::53:0;:20:::1;;:16::54;18:20:18:21:0;:23:20:17;19:32:19:41;;:55::56;;:44::63:1;:32;:20::64;;;;;;;;;;;20:23:22:17:0;21:33:21:43;;:::47:1;:20::48;;;;;;;;20:23:22:17;15:19:25:13;8:11:26:9;26:17::18:0;:21::37;8:8::39:1;;28:16:28:36:0;::::1;:8::38;29:16:29:26:0;:29::30;:8::32:1;2:22:30:5', + bytecode: '000000006576c6547a757c727b8bc46e9f76635279d100879177766354795379cc93557a757c6b7c727b6c67537a8b7c727b68686d76c3a26672a0697c00a077', + sourceMap: '3:23:3:24;4:24:4:25;5:25:5:26;6:16:6:17;8:8:26:39;9:33:9:34;:23::41:1;:12::42;;;;;;10::10:22;12:20:12:37:0;13:21:13:26;::::1;15:16:15:17:0;:19:25:13;16:31:16:32;;:20::47:1;:51::53:0;:20:::1;;:16::54;18:20:18:21:0;:23:20:17;19:32:19:41;;:55::56;;:44::63:1;:32;:20::64;;;;;;;;;20:23:22:17:0;21:20:21:48:1;;;;;;20:23:22:17;15:19:25:13;8:11:26:9;26:17::18:0;:21::37;8:8::39:1;;28:16:28:36:0;::::1;:8::38;29:16:29:26:0;:29::30;:8::32:1;2:22:30:5', logs: [ - { ip: 60, line: 24, data: [{ stackIndex: 3, type: 'int', ip: 60 }] }, + { ip: 51, line: 24, data: [{ stackIndex: 3, type: 'int', ip: 51 }] }, ], requires: [ - { ip: 68, line: 28 }, - { ip: 72, line: 29 }, + { ip: 59, line: 28 }, + { ip: 63, line: 29 }, ], - sourceTags: '61:61:sc;72:72:sc', + sourceTags: '52:52:sc;63:63:sc', }, - fingerprint: 'b91e25a054a9cad4ddfb496c397c5d7e7e24a7ed131326b91e93d48bbee00e24', + fingerprint: '0dfd8b49c1d5e3af83759f9921d61901e5dbfa99e2b2afbe490e9bb3fba813f5', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/deep_increment_decrement.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/deep_increment_decrement.ts new file mode 100644 index 000000000..24143b19b --- /dev/null +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/deep_increment_decrement.ts @@ -0,0 +1,78 @@ +import { Fixture } from '../../fixture-utils.js'; + +export const fixtures: Fixture[] = [ + { + artifact: { + contractName: 'DeepIncrementDecrement', + constructorInputs: [], + abi: [ + { name: 'loop', inputs: [{ name: 'budget', type: 'int' }] }, + { + name: 'branch', + inputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'int' }, { name: 'z', type: 'int' }], + }, + ], + bytecode: + // function loop(int budget) { + 'OP_DUP OP_0 OP_NUMEQUAL OP_IF ' + // int remaining = budget; + + 'OP_OVER ' + // int steps = 0; + + 'OP_0 ' + // int a = 1; + + 'OP_1 ' + // int b = 2; + + 'OP_2 ' + // int c = 3; + + 'OP_3 ' + // for (int i = 0; i < 3; i++) { + + 'OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF ' + // remaining = remaining - 1; + + 'OP_5 OP_ROLL OP_1SUB OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_2SWAP OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK ' + // steps = steps + 1; + + 'OP_4 OP_ROLL OP_1ADD OP_SWAP OP_TOALTSTACK OP_SWAP OP_2SWAP OP_ROT OP_FROMALTSTACK ' + // i++ + + 'OP_1ADD ' + // Loop condition + + 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL ' + // Cleanup + + 'OP_DROP ' + // require(a + b + c == 6); + + 'OP_ROT OP_ROT OP_ADD OP_ADD OP_6 OP_NUMEQUALVERIFY ' + // require(remaining + steps == budget); + + 'OP_ADD OP_ROT OP_NUMEQUAL ' + // Cleanup + + 'OP_NIP ' + // } + + 'OP_ELSE ' + // function branch(int x, int y, int z) { + + 'OP_1 OP_NUMEQUALVERIFY ' + // if (x > y) { + + 'OP_2DUP OP_LESSTHAN OP_IF ' + // x = x - 1; + + 'OP_1SUB ' + // y = y - 1; + + 'OP_SWAP OP_1SUB OP_SWAP ' + // z = z - 1; + + 'OP_ROT OP_1SUB OP_ROT OP_ROT ' + // } + + 'OP_ENDIF ' + // require(x + y + z > 0); + + 'OP_ADD OP_ADD OP_0 OP_GREATERTHAN ' + // } + + 'OP_ENDIF', + debug: { + bytecode: '76009c637800515253006576539f766b63557a8c7c6b7c6b7c727b6c6c547a8b7c6b7c727b6c8b686c9166757b7b9393569d937b9c7767519d6e9f638c7c8c7c7b8c7b7b68939300a068', + sourceMap: '2:4:16:5;;;;3:24:3:30;4:20:4:21;5:16:5:17;6::6;7::7;9:21:9:22;:8:12:9;:24:9:25;:28::29;:24:::1;;;:36:12:9:0;10:12:10:38:1;;;;;;;;;;;;11::11:30;;;;;;;;;9:31:9:34;:36:12:9;;:8;;;14:16:14:17:0;:20::21;:16:::1;:::25;:29::30:0;:8::32:1;15:16:15:33;:37::43:0;:8::45:1;2:30:16:5;:4;18::26::0;;19:12:19:17;::::1;:19:23:9:0;20:12:20:22:1;21::21;;;22::22;;;;19:19:23:9;25:16:25:21;:::25;:28::29:0;:8::31:1;1:0:27:1', + logs: [], + requires: [ + { ip: 49, line: 14 }, + { ip: 53, line: 15 }, + { ip: 73, line: 25 }, + ], + sourceTags: '38:38:fu;39:42:lc;43:43:sc;53:53:sc', + }, + fingerprint: '98cc2394f050f139ea1b4e5816109403122c1381f60fb2379833fd772283fcbd', + }, + }, +]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/deep_replace.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/deep_replace.ts index bd8694560..bf281aa57 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/deep_replace.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/deep_replace.ts @@ -12,18 +12,18 @@ export const fixtures: Fixture[] = [ // if (a < 3) { + 'OP_5 OP_PICK OP_3 OP_LESSTHAN OP_IF ' // a = 3 } - + 'OP_3 OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP ' - + 'OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF ' + + 'OP_3 OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_2SWAP ' + + 'OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF ' // require(a > b + c + d + e + f); + 'OP_2ROT OP_5 OP_ROLL OP_ADD OP_4 OP_ROLL OP_ADD ' + 'OP_3 OP_ROLL OP_ADD OP_ROT OP_ADD OP_GREATERTHAN', debug: { - bytecode: '5152535455565579539f6353567a757c6b7c6b7c6b7b7b6c6c6c6871557a93547a93537a937b93a0', + bytecode: '5152535455565579539f6353567a757c6b7c6b7c727b6c6c6871557a93547a93537a937b93a0', logs: [], - requires: [{ ip: 40, line: 14 }], - sourceMap: '3:16:3:17;4::4;5::5;6::6;7::7;8::8;10:12:10:13;;:16::17;:12:::1;:19:12:9:0;11:16:11:17;:12::18:1;;;;;;;;;;;;;;10:19:12:9;14:16:14:21:0;:24::25;;:20:::1;:28::29:0;;:20:::1;:32::33:0;;:20:::1;:36::37:0;:20:::1;:8::39', + requires: [{ ip: 38, line: 14 }], + sourceMap: '3:16:3:17;4::4;5::5;6::6;7::7;8::8;10:12:10:13;;:16::17;:12:::1;:19:12:9:0;11:16:11:17;:12::18:1;;;;;;;;;;;;10:19:12:9;14:16:14:21:0;:24::25;;:20:::1;:28::29:0;;:20:::1;:32::33:0;;:20:::1;:36::37:0;:20:::1;:8::39', }, - fingerprint: 'e586fb63aa0c9791cdf96f5472328a702565bd0e1da1bb101dc2619c5655f465', + fingerprint: '998cd5c0cd9a0da56cdbc8f0220ac98a3f33bf91fba3ce7f454c70144c3ec4b9', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_no_introspection.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_no_introspection.ts index 485a6a804..000fff1c6 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_no_introspection.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/do_while_loop_no_introspection.ts @@ -6,18 +6,18 @@ export const fixtures: Fixture[] = [ contractName: 'Loopy', constructorInputs: [], abi: [{ name: 'doLoop', inputs: [] }], - bytecode: 'OP_0 OP_2 OP_BEGIN OP_OVER OP_1ADD OP_ROT OP_DROP OP_SWAP OP_2DUP OP_ADD OP_10 OP_LESSTHAN OP_VERIFY OP_OVER OP_10 OP_GREATERTHANOREQUAL OP_UNTIL OP_2DROP OP_1', + bytecode: 'OP_0 OP_2 OP_BEGIN OP_SWAP OP_1ADD OP_SWAP OP_2DUP OP_ADD OP_10 OP_LESSTHAN OP_VERIFY OP_OVER OP_10 OP_GREATERTHANOREQUAL OP_UNTIL OP_2DROP OP_1', debug: { - bytecode: '005265788b7b757c6e935a9f69785aa2666d51', - sourceMap: '3:16:3:17;4::4;6:8:10:25;7:16:7:17;:::21:1;:12::22;;;9:20:9:25:0;::::1;:28::30:0;:20:::1;:12::32;10:17:10:18:0;:21::23;6:8::25:1;;2:22:11:5;', + bytecode: '0052657c8b7c6e935a9f69785aa2666d51', + sourceMap: '3:16:3:17;4::4;6:8:10:25;7:12:7:22:1;;;9:20:9:25:0;::::1;:28::30:0;:20:::1;:12::32;10:17:10:18:0;:21::23;6:8::25:1;;2:22:11:5;', logs: [ - { ip: 8, line: 8, data: [{ stackIndex: 1, type: 'int', ip: 8 }] }, + { ip: 6, line: 8, data: [{ stackIndex: 1, type: 'int', ip: 6 }] }, ], requires: [ - { ip: 12, line: 9 }, + { ip: 10, line: 9 }, ], }, - fingerprint: '39b67bb19440620390b83e3bc1e638ea6827998f4a14c2d6e29c2afa9815b2fd', + fingerprint: 'c593e0b3cba73663f3bb5fcbcdba620c02ce0e9dce951ff4e52b89c27a90ad6b', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/double_negation.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/double_negation.ts index eb39fbb22..abb1f9794 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/double_negation.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/double_negation.ts @@ -12,9 +12,9 @@ export const fixtures: Fixture[] = [ // int i = 0; bool done = false; + 'OP_0 OP_0 ' // do { i = i + 1; - + 'OP_BEGIN OP_OVER OP_1ADD OP_ROT OP_DROP OP_SWAP ' + + 'OP_BEGIN OP_SWAP OP_1ADD OP_TUCK ' // done = i >= target; - + 'OP_OVER OP_4 OP_PICK OP_GREATERTHANOREQUAL OP_NIP ' + + 'OP_4 OP_PICK OP_GREATERTHANOREQUAL OP_NIP ' // } while (!done) - OP_NOT OP_NOT OP_UNTIL is optimised to OP_UNTIL + 'OP_DUP OP_UNTIL ' // if (!!flag) - OP_NOT OP_NOT OP_IF becomes OP_NOT OP_NOTIF, which is optimised to OP_IF @@ -26,17 +26,17 @@ export const fixtures: Fixture[] = [ // clean up i and done + 'OP_NIP OP_NIP', debug: { - bytecode: '7669000065788b7b757c785479a27776667b637853799d687c00a07777', - sourceMap: '4:18:4:22;:8::24:1;6:16:6:17:0;7:20:7:25;9:8:13:24;10:16:10:17;:::21:1;:12::22;;;11:19:11:20:0;:24::30;;:19:::1;:12::31;13:18:13:22:0;9:8::24:1;16:14:16:18:0;:12:18:9;17:20:17:21;:25::31;;:12::33:1;16:20:18:9;20:16:20:17:0;:20::21;:8::23:1;2:31:21:5;', + bytecode: '76690000657c8b7d5479a27776667b637853799d687c00a07777', + sourceMap: '4:18:4:22;:8::24:1;6:16:6:17:0;7:20:7:25;9:8:13:24;10:12:10:22:1;;::11:20:0;11:24::30;;:19:::1;:12::31;13:18:13:22:0;9:8::24:1;16:14:16:18:0;:12:18:9;17:20:17:21;:25::31;;:12::33:1;16:20:18:9;20:16:20:17:0;:20::21;:8::23:1;2:31:21:5;', logs: [], requires: [ { ip: 2, line: 4 }, - { ip: 23, line: 17 }, - { ip: 28, line: 20 }, + { ip: 20, line: 17 }, + { ip: 25, line: 20 }, ], - sourceTags: '27:28:sc', + sourceTags: '24:25:sc', }, - fingerprint: 'ed0b5bc35f0130fa04d1fce813fbd7c183bb5346006d10c0808f0d9872712ded', + fingerprint: '5dd89e0fc04feb9dd9c02f890421b3883befee8f91a18ac05aee444d377ac4fc', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/everything.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/everything.ts index 30c7083c9..4876c8f30 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/everything.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/everything.ts @@ -14,7 +14,7 @@ export const fixtures: Fixture[] = [ // int myVariable = 10 - int(false); // they can go at the end of the line + 'OP_10 OP_0 OP_SUB ' // int myOtherVariable = (i + myVariable) % 2; - + 'OP_2 OP_PICK OP_OVER OP_ADD OP_2 OP_MOD ' + + 'OP_3DUP OP_NIP OP_ADD OP_2 OP_MOD ' // require(myOtherVariable /* And comments can be included within lines */ > i); + 'OP_DUP OP_4 OP_PICK OP_GREATERTHAN OP_VERIFY ' // myOtherVariable = i; @@ -30,7 +30,7 @@ export const fixtures: Fixture[] = [ // if (i > 400) { + 'OP_4 OP_PICK 9001 OP_GREATERTHAN OP_IF ' // i = 400; - + '9001 OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK ' + + '9001 OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_2SWAP OP_ROT OP_FROMALTSTACK ' // } + 'OP_ENDIF ' // if (x > 10) { @@ -47,20 +47,20 @@ export const fixtures: Fixture[] = [ + 'OP_ELSE OP_DUP OP_1 OP_NUMEQUALVERIFY OP_ENDIF OP_ENDIF ' // } + 'OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_1', - fingerprint: 'db741271dde641b395ed7d3f282179c607d56f29b2ba7cddff3b7670a24072e2', + fingerprint: 'cff254ff1a7e9f95dfcac838cb9ac1ca6f75cc15db4ec1a19cb2215e1ed9759f', debug: { - bytecode: '02900178930807364897987fe80753797e5a0094527978935297765479a06953795a547aa678a68802f401b275567a8277018a9f695479029001a063029001557a757c6b7c6b7b7b6c6c6855795aa063547901149f6956795879ad675579559f6300696776519d68686d6d6d6d51', - sourceMap: '9:16:9:19;:22::23;:16:::1;10:18:10:36:0;:45::46;;:18::47:1;12:25:12:27:0;:34::39;:25::40:1;13:31:13:32:0;;:35::45;:31:::1;:49::50:0;:30:::1;14:16:14:31:0;:82::83;;:16:::1;:8::85;16:26:16:27:0;;17:21:17:23;19:26:19:27;;:16::28:1;:48::58:0;:32::60:1;:8::62;20:28:20:31:0;:8::33:1;;21:16:21:17:0;;:::24:1;;:27::30:0;:16:::1;:8::32;23:12:23:13:0;;:16::19;:12:::1;:21:25:9:0;24:16:24:19;:12::20:1;;;;;;;;;;;23:21:25:9;27:12:27:13:0;;:16::18;:12:::1;:20:30:9:0;28::28:21;;:24::26;:20:::1;:12::28;29:29:29:30:0;;:32::34;;:12::37:1;30:15:33::0;:19:30:20;;:23::24;:19:::1;:26:32:9:0;31:20:31:25;:12::27:1;33::33:37:0;:20::30;:34::35;:12::37:1;;30:15;8:37:34:5;;;;', + bytecode: '02900178930807364897987fe80753797e5a00946f77935297765479a06953795a547aa678a68802f401b275567a8277018a9f695479029001a063029001557a757c6b7c727b6c6855795aa063547901149f6956795879ad675579559f6300696776519d68686d6d6d6d51', + sourceMap: '9:16:9:19;:22::23;:16:::1;10:18:10:36:0;:45::46;;:18::47:1;12:25:12:27:0;:34::39;:25::40:1;13:31:13:45:0;;::::1;:49::50:0;:30:::1;14:16:14:31:0;:82::83;;:16:::1;:8::85;16:26:16:27:0;;17:21:17:23;19:26:19:27;;:16::28:1;:48::58:0;:32::60:1;:8::62;20:28:20:31:0;:8::33:1;;21:16:21:17:0;;:::24:1;;:27::30:0;:16:::1;:8::32;23:12:23:13:0;;:16::19;:12:::1;:21:25:9:0;24:16:24:19;:12::20:1;;;;;;;;;23:21:25:9;27:12:27:13:0;;:16::18;:12:::1;:20:30:9:0;28::28:21;;:24::26;:20:::1;:12::28;29:29:29:30:0;;:32::34;;:12::37:1;30:15:33::0;:19:30:20;;:23::24;:19:::1;:26:32:9:0;31:20:31:25;:12::27:1;33::33:37:0;:20::30;:34::35;:12::37:1;;30:15;8:37:34:5;;;;', logs: [], requires: [ - { ip: 22, line: 14 }, - { ip: 31, line: 19 }, - { ip: 33, line: 20 }, - { ip: 41, line: 21 }, - { ip: 69, line: 28 }, - { ip: 74, line: 29 }, - { ip: 82, line: 31 }, - { ip: 86, line: 33 }, + { ip: 21, line: 14 }, + { ip: 30, line: 19 }, + { ip: 32, line: 20 }, + { ip: 40, line: 21 }, + { ip: 66, line: 28 }, + { ip: 71, line: 29 }, + { ip: 79, line: 31 }, + { ip: 83, line: 33 }, ], }, }, diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_stack_items.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_stack_items.ts index 760c05b14..5daa5ef8c 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_stack_items.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/for_loop_stack_items.ts @@ -6,19 +6,19 @@ export const fixtures: Fixture[] = [ contractName: 'ForLoopBasic', constructorInputs: [], abi: [{ name: 'spend', inputs: [] }], - bytecode: 'OP_0 OP_1 OP_1 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_3 OP_PICK OP_OVER OP_ADD OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_ROT OP_3 OP_NUMEQUALVERIFY OP_SWAP OP_1 OP_NUMEQUALVERIFY OP_1 OP_NUMEQUAL', + bytecode: 'OP_0 OP_1 OP_1 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_3 OP_PICK OP_OVER OP_ADD OP_4 OP_ROLL OP_DROP OP_SWAP OP_2SWAP OP_ROT OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_ROT OP_3 OP_NUMEQUALVERIFY OP_SWAP OP_1 OP_NUMEQUALVERIFY OP_1 OP_NUMEQUAL', debug: { - bytecode: '005151006576539f766b6353797893547a757c6b7b7b6c8b686c9166757b539d7c519d519c', - sourceMap: '3:18:3:19;4:16:4:17;5::5;7:21:7:22;:8:9:9;:24:7:25;:28::29;:24:::1;;;:39:9:9:0;8:18:8:21;;:24::25;:18:::1;:12::26;;;;;;;;7:31:7:37;:39:9:9;;:8;;;11:16:11:19:0;:23::24;:8::26:1;12:16:12:17:0;:21::22;:8::24:1;13:21:13:22:0;:8::24:1', + bytecode: '005151006576539f766b6353797893547a757c727b8b686c9166757b539d7c519d519c', + sourceMap: '3:18:3:19;4:16:4:17;5::5;7:21:7:22;:8:9:9;:24:7:25;:28::29;:24:::1;;;:39:9:9:0;8:18:8:21;;:24::25;:18:::1;:12::26;;;;;;7:31:7:37;:39:9:9;;:8;;;11:16:11:19:0;:23::24;:8::26:1;12:16:12:17:0;:21::22;:8::24:1;13:21:13:22:0;:8::24:1', logs: [], requires: [ - { ip: 31, line: 11 }, - { ip: 34, line: 12 }, - { ip: 37, line: 13 }, + { ip: 29, line: 11 }, + { ip: 32, line: 12 }, + { ip: 35, line: 13 }, ], - sourceTags: '23:23:fu;24:27:lc;28:28:sc', + sourceTags: '21:21:fu;22:25:lc;26:26:sc', }, - fingerprint: 'd15bc8a0fcecc3388e80e770890c8d097045a99258ffad7d409a306bae42513e', + fingerprint: 'daa7f96c846a6af3366dd7a2c5597d29524665dce0cc9a5fc5e2a12c97babe2f', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/for_while_nested.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/for_while_nested.ts index 55067cc19..f2ee5e92d 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/for_while_nested.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/for_while_nested.ts @@ -6,30 +6,30 @@ export const fixtures: Fixture[] = [ contractName: 'ForWhileNested', constructorInputs: [], abi: [{ name: 'spend', inputs: [] }], - bytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_ROT OP_ROT OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_4 OP_NUMEQUAL', + bytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_3DUP OP_DROP OP_ADD OP_OVER OP_ADD OP_2SWAP OP_NIP OP_ROT OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_SWAP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_4 OP_NUMEQUAL', debug: { - bytecode: '00006576529f766b63006576529f766b6352795279937893537a757b7b8b686c9166788b7b7577686c916675549c', - sourceMap: '3:18:3:19;5:21:5:22;:8:13:9;:24:5:25;:28::29;:24:::1;;;:42:13:9:0;6:20:6:21;8:12:12:13;:19:8:20;:23::24;:19:::1;;;:26:12:13:0;9:22:9:25;;:28::29;;:22:::1;:32::33:0;:22:::1;:16::34;;;;;10::10:26;8:26:12:13;;:12;;5:35:5:36:0;:::40:1;:31;;::13:9;:42;;:8;;;15:23:15:24:0;:8::26:1', + bytecode: '00006576529f766b63006576529f766b636f7593789372777b8b686c91667c8b77686c916675549c', + sourceMap: '3:18:3:19;5:21:5:22;:8:13:9;:24:5:25;:28::29;:24:::1;;;:42:13:9:0;6:20:6:21;8:12:12:13;:19:8:20;:23::24;:19:::1;;;:26:12:13:0;9:22:9:29;;::::1;:32::33:0;:22:::1;:16::34;;;10::10:26;8:26:12:13;;:12;;5:31:5:40;;::13:9;:42;;:8;;;15:23:15:24:0;:8::26:1', logs: [ { - ip: 30, + ip: 26, line: 11, data: [ 'sum:', - { stackIndex: 2, type: 'int', ip: 30 }, + { stackIndex: 2, type: 'int', ip: 26 }, 'i:', - { stackIndex: 1, type: 'int', ip: 30 }, + { stackIndex: 1, type: 'int', ip: 26 }, 'j:', - { stackIndex: 0, type: 'int', ip: 30 }, + { stackIndex: 0, type: 'int', ip: 26 }, ], }, ], requires: [ - { ip: 46, line: 15 }, + { ip: 40, line: 15 }, ], - sourceTags: '30:33:lc;34:38:fu;39:42:lc;43:43:sc', + sourceTags: '26:29:lc;30:32:fu;33:36:lc;37:37:sc', }, - fingerprint: '6e3242d4469324cf4c6a1b2186dce828984c74bbdc05c8a5abd1917142b4c5e9', + fingerprint: '12f233fbd4c8ffef111e10c2a36ed6b3ef3a83baa5a692665392ad9b2021a56f', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_altstack_cleanup.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_altstack_cleanup.ts index 89579712f..01c2339c0 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_altstack_cleanup.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/global_function_altstack_cleanup.ts @@ -8,28 +8,28 @@ export const fixtures: Fixture[] = [ abi: [{ name: 'spend', inputs: [{ name: 'x', type: 'int' }] }], bytecode: // int lo, int hi = orderedPair(1, 2, x); - 'OP_1 OP_2 OP_ROT OP_0 OP_0 OP_ROT OP_0 OP_GREATERTHAN OP_IF OP_3 OP_PICK OP_ROT OP_DROP OP_SWAP OP_2 OP_PICK OP_NIP OP_ELSE OP_NIP OP_OVER OP_SWAP OP_3 OP_PICK OP_NIP OP_ENDIF OP_ROT OP_DROP OP_ROT OP_DROP ' + 'OP_1 OP_2 OP_ROT OP_0 OP_0 OP_ROT OP_0 OP_GREATERTHAN OP_IF OP_3 OP_PICK OP_NIP OP_NIP OP_OVER OP_ELSE OP_NIP OP_OVER OP_SWAP OP_3 OP_PICK OP_NIP OP_ENDIF OP_ROT OP_DROP OP_ROT OP_DROP ' // require(lo == 1, "lo should be 1"); + 'OP_SWAP OP_1 OP_NUMEQUALVERIFY ' // require(hi == 2, "hi should be 2"); + 'OP_2 OP_NUMEQUAL', - fingerprint: '641437fc5e376f92f1da865a9a0e8a1cc1a48fe845b38ea10afa9ba4ce4e5908', + fingerprint: 'cf461d3f220cab87174e9b316dc5de08f9caf4389b1844fe1ffa0d24a55701e1', debug: { - bytecode: '51527b00007b00a06353797b757c5279776777787c537977687b757b757c519d529c', - sourceMap: '16:37:16:38;:40::41;:43::44;:25::45:1;;;;;;;;;;;;;;;;;;;;;;;;;;17:16:17:18:0;:22::23;:8::43:1;18:22:18:23:0;:8::43:1', + bytecode: '51527b00007b00a06353797777786777787c537977687b757b757c519d529c', + sourceMap: '16:37:16:38;:40::41;:43::44;:25::45:1;;;;;;;;;;;;;;;;;;;;;;;17:16:17:18:0;:22::23;:8::43:1;18:22:18:23:0;:8::43:1', logs: [], - requires: [{ ip: 31, line: 17, message: 'lo should be 1' }, { ip: 34, line: 18, message: 'hi should be 2' }], + requires: [{ ip: 28, line: 17, message: 'lo should be 1' }, { ip: 31, line: 18, message: 'hi should be 2' }], functions: [ { name: 'orderedPair', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'int' }, { name: 'c', type: 'int' }], - bytecode: '00007b00a06353797b757c5279776777787c537977687b757b75', - sourceMap: '2:13:2:14;3::3;4:8:4:9;:12::13;:8:::1;:15:7:5:0;5:13:5:14;;:8::15:1;;;6:13:6:14:0;;:8::15:1;7:11:10:5:0;8:8:8:15:1;;;9:13:9:14:0;;:8::15:1;7:11:10:5;1:61:12:1;;;', + bytecode: '00007b00a06353797777786777787c537977687b757b75', + sourceMap: '2:13:2:14;3::3;4:8:4:9;:12::13;:8:::1;:15:7:5:0;5:13:5:14;;:8:6:15:1;;6;7:11:10:5:0;8:8:8:15:1;;;9:13:9:14:0;;:8::15:1;7:11:10:5;1:61:12:1;;;', logs: [], requires: [], }, ], - inlineRanges: '3:28:orderedPair', + inlineRanges: '3:25:orderedPair', }, }, }, diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_statements.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_statements.ts index 104434604..2110f7229 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_statements.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/multiline_statements.ts @@ -6,18 +6,18 @@ export const fixtures: Fixture[] = [ contractName: 'MultilineStatements', constructorInputs: [{ name: 'x', type: 'int' }, { name: 'y', type: 'string' }], abi: [{ name: 'spend', inputs: [{ name: 'a', type: 'int' }, { name: 'b', type: 'string' }] }], - bytecode: 'OP_ROT OP_SWAP OP_2 OP_SUB OP_NUMEQUAL OP_2 OP_PICK OP_2 OP_PICK OP_EQUAL OP_BOOLAND OP_IF OP_0 OP_VERIFY OP_ELSE OP_OVER 48656c6c6f20 OP_2 OP_PICK OP_CAT OP_EQUAL OP_IF OP_DUP 576f726c64 OP_EQUALVERIFY OP_ELSE OP_1 OP_0 OP_NOT OP_NOT OP_NOT OP_NUMEQUALVERIFY OP_ENDIF OP_ENDIF OP_2DROP OP_1', + bytecode: 'OP_ROT OP_SWAP OP_2 OP_SUB OP_NUMEQUAL OP_3DUP OP_DROP OP_EQUAL OP_BOOLAND OP_IF OP_0 OP_VERIFY OP_ELSE OP_OVER 48656c6c6f20 OP_2 OP_PICK OP_CAT OP_EQUAL OP_IF OP_DUP 576f726c64 OP_EQUALVERIFY OP_ELSE OP_1 OP_0 OP_NOT OP_NOT OP_NOT OP_NUMEQUALVERIFY OP_ENDIF OP_ENDIF OP_2DROP OP_1', debug: { - bytecode: '7b7c52949c52795279879a63006967780648656c6c6f2052797e87637605576f726c64886751009191919d68686d51', + bytecode: '7b7c52949c6f75879a63006967780648656c6c6f2052797e87637605576f726c64886751009191919d68686d51', logs: [], requires: [ - { ip: 15, line: 11 }, - { ip: 26, line: 14 }, - { ip: 33, line: 18 }, + { ip: 13, line: 11 }, + { ip: 24, line: 14 }, + { ip: 31, line: 18 }, ], - sourceMap: '9:12:9:13;:17::18;:21::22;:17:::1;:12;10::10:13:0;;:17::18;;:12:::1;9;10:20:12:9:0;11::11:25;:12::27:1;12:15:19:9:0;:19:12:20;:24::32;13:10:13:11;;12:24:::1;:19;13:13:17:9:0;15:16:15:17;:21::28;14:12:16:14:1;17:15:19:9:0;18:20:18:24;:31::36;:30:::1;:29;:28;:12::38;17:15:19:9;12;8:6:20:5;', + sourceMap: '9:12:9:13;:17::18;:21::22;:17:::1;:12;10::10:18:0;;::::1;9;10:20:12:9:0;11::11:25;:12::27:1;12:15:19:9:0;:19:12:20;:24::32;13:10:13:11;;12:24:::1;:19;13:13:17:9:0;15:16:15:17;:21::28;14:12:16:14:1;17:15:19:9:0;18:20:18:24;:31::36;:30:::1;:29;:28;:12::38;17:15:19:9;12;8:6:20:5;', }, - fingerprint: 'd2aa37425c07883d3c7df823103f88a6a061d3efca1e6317b9e72e0ad4bd3611', + fingerprint: '1d2c7a186db7be1715a2e6dbf01d56c005c8e0a1cd78233688881aa6312ea0c2', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/split_size.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/split_size.ts index ff0195029..fd6b062dc 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/split_size.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/split_size.ts @@ -8,21 +8,21 @@ export const fixtures: Fixture[] = [ abi: [{ name: 'spend', inputs: [] }], bytecode: // bytes x = b.split(b.length / 2)[1] - 'OP_DUP OP_DUP OP_SIZE OP_NIP OP_2 OP_DIV OP_SPLIT OP_NIP ' + 'OP_DUP OP_SIZE OP_2 OP_DIV OP_SPLIT OP_NIP ' // require(x != b) + 'OP_2DUP OP_EQUAL OP_NOT OP_VERIFY ' // bytes x = b.split(b.length / 2)[1] + 'OP_SWAP OP_4 OP_SPLIT OP_DROP OP_EQUAL OP_NOT', debug: { - bytecode: '7676827752967f776e8791697c547f758791', + bytecode: '768252967f776e8791697c547f758791', logs: [], requires: [ - { ip: 12, line: 4 }, - { ip: 19, line: 5 }, + { ip: 10, line: 4 }, + { ip: 17, line: 5 }, ], - sourceMap: '3:18:3:27;;:26::34:1;;:37::38:0;:26:::1;:18::39;:::42;4:16:4:22:0;::::1;;:8::24;5:16:5:17:0;:24::25;:16::26:1;:::29;:::34;:8::36', + sourceMap: '3:18:3:27;:::34:1;:37::38:0;:26:::1;:18::39;:::42;4:16:4:22:0;::::1;;:8::24;5:16:5:17:0;:24::25;:16::26:1;:::29;:::34;:8::36', }, - fingerprint: '49d50376d8aa76534f29c049b987238d577ce332202a2f0da0cb74d94a027b79', + fingerprint: '2df4a139558bd0eac768c9361f8fc16781906f7dcb42f8c2ff6b0459da0bbcfd', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment.ts index faa537c29..0c2123714 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment.ts @@ -10,17 +10,17 @@ export const fixtures: Fixture[] = [ contractName: 'TupleReassignment', constructorInputs: [], abi: [{ name: 'spend', inputs: [{ name: 'seed', type: 'int' }] }], - bytecode: 'OP_DUP OP_1ADD OP_2DUP OP_SWAP OP_2DUP OP_SWAP OP_0 OP_BEGIN OP_DUP OP_4 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_SWAP OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK OP_ROT OP_DROP OP_SWAP OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_DUP OP_4 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_OVER OP_2 OP_PICK OP_3 OP_ROLL OP_MUL OP_1ADD OP_TOALTSTACK OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK OP_ROT OP_DROP OP_SWAP OP_FROMALTSTACK OP_DUP OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_OVER OP_2 OP_PICK OP_3 OP_ROLL OP_MUL OP_1ADD OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_TOALTSTACK OP_ROT OP_DROP OP_SWAP OP_FROMALTSTACK OP_DUP OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_2DUP OP_ROT OP_ROT OP_ADD OP_ROT OP_ADD OP_ADD OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_2DROP OP_2DROP OP_1', + bytecode: 'OP_DUP OP_1ADD OP_2DUP OP_SWAP OP_2DUP OP_SWAP OP_0 OP_BEGIN OP_DUP OP_4 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_SWAP OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_2SWAP OP_ROT OP_FROMALTSTACK OP_ROT OP_DROP OP_SWAP OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_DUP OP_4 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_OVER OP_2 OP_PICK OP_3 OP_ROLL OP_MUL OP_1ADD OP_TOALTSTACK OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_2SWAP OP_ROT OP_FROMALTSTACK OP_ROT OP_DROP OP_SWAP OP_FROMALTSTACK OP_DUP OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_SWAP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_4 OP_PICK OP_OVER OP_2 OP_PICK OP_3 OP_ROLL OP_MUL OP_1ADD OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_2SWAP OP_ROT OP_FROMALTSTACK OP_FROMALTSTACK OP_TOALTSTACK OP_ROT OP_DROP OP_SWAP OP_FROMALTSTACK OP_DUP OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_SWAP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_2DUP OP_ROT OP_ROT OP_ADD OP_ROT OP_ADD OP_ADD OP_0 OP_GREATERTHANOREQUAL OP_VERIFY OP_2DROP OP_2DROP OP_1', debug: { - bytecode: '768b6e7c6e7c006576549f766b637854797c557a757c6b7c6b7b7b6c6c7b757c8b686c916675006576549f766b63785479785279537a958b6b557a757c6b7c6b7b7b6c6c7b757c6c7600a269788b7b7577686c916675006576529f766b63785479785279537a958b567a757c6b7c6b7c6b7b7b6c6c6c6b7b757c6c7600a269788b7b7577686c9166756e7b7b937b939300a2696d6d51', - sourceMap: '17:16:18:20;18:::24:1;21:22:21:26:0;:17::27:1;24:26:24:30:0;:21::31:1;27::27:22:0;:8:29:9;:24:27:25;:28::29;:24:::1;;;:42:29:9:0;28:26:28:27;:29::30;;:21::31:1;:12::32;;;;;;;;;;;;;;27:31:27:40;:42:29:9;;:8;;;33:21:33:22:0;:8:36:9;:24:33:25;:28::29;:24:::1;;;:42:36:9:0;34:34:34:35;:37::38;;:29::39:1;;;;;;;:12::40;;;;;;;;;;;;;;;;35:20:35:22:0;:26::27;:20:::1;:12::29;33:35:33:36:0;:::40:1;:31;;::36:9;:42;;:8;;;40:21:40:22:0;:8:43:9;:24:40:25;:28::29;:24:::1;;;:42:43:9:0;41:34:41:35;:37::38;;:29::39:1;;;;;;;:12::40;;;;;;;;;;;;;;;;;;;42:20:42:22:0;:26::27;:20:::1;:12::29;40:35:40:36:0;:::40:1;:31;;::43:9;:42;;:8;;;46:24:46:28:0;48:16:48:17;:20::21;:16:::1;:24::25:0;:16:::1;:::29;:33::34:0;:16:::1;:8::36;16:29:49:5;;', + bytecode: '768b6e7c6e7c006576549f766b637854797c557a757c6b7c727b6c7b757c8b686c916675006576549f766b63785479785279537a958b6b557a757c6b7c727b6c7b757c6c7600a2697c8b77686c916675006576529f766b63785479785279537a958b567a757c6b7c6b7c727b6c6c6b7b757c6c7600a2697c8b77686c9166756e7b7b937b939300a2696d6d51', + sourceMap: '17:16:18:20;18:::24:1;21:22:21:26:0;:17::27:1;24:26:24:30:0;:21::31:1;27::27:22:0;:8:29:9;:24:27:25;:28::29;:24:::1;;;:42:29:9:0;28:26:28:27;:29::30;;:21::31:1;:12::32;;;;;;;;;;;;27:31:27:40;:42:29:9;;:8;;;33:21:33:22:0;:8:36:9;:24:33:25;:28::29;:24:::1;;;:42:36:9:0;34:34:34:35;:37::38;;:29::39:1;;;;;;;:12::40;;;;;;;;;;;;;;35:20:35:22:0;:26::27;:20:::1;:12::29;33:31:33:40;;::36:9;:42;;:8;;;40:21:40:22:0;:8:43:9;:24:40:25;:28::29;:24:::1;;;:42:43:9:0;41:34:41:35;:37::38;;:29::39:1;;;;;;;:12::40;;;;;;;;;;;;;;;;;42:20:42:22:0;:26::27;:20:::1;:12::29;40:31:40:40;;::43:9;:42;;:8;;;46:24:46:28:0;48:16:48:17;:20::21;:16:::1;:24::25:0;:16:::1;:::29;:33::34:0;:16:::1;:8::36;16:29:49:5;;', logs: [], requires: [ - { ip: 75, line: 35 }, - { ip: 126, line: 42 }, - { ip: 146, line: 48 }, + { ip: 71, line: 35 }, + { ip: 118, line: 42 }, + { ip: 136, line: 48 }, ], - sourceTags: '32:32:fu;33:36:lc;37:37:sc;76:80:fu;81:84:lc;85:85:sc;127:131:fu;132:135:lc;136:136:sc', + sourceTags: '30:30:fu;31:34:lc;35:35:sc;72:74:fu;75:78:lc;79:79:sc;119:121:fu;122:125:lc;126:126:sc', functions: [ { name: 'swap', @@ -39,9 +39,9 @@ export const fixtures: Fixture[] = [ requires: [], }, ], - inlineRanges: '3:3:swap;5:5:swap;17:17:swap;49:55:step;97:103:step;138:138:swap', + inlineRanges: '3:3:swap;5:5:swap;17:17:swap;47:53:step;91:97:step;128:128:swap', }, - fingerprint: '0c488b42372749a5a7278c738799b229e4346abe52a7172d3d1813764dbc67e2', + fingerprint: '16a15dba45f221df3828786838f63c5928154d9339311d6467f79a8f25cb3093', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_after_final_read.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_after_final_read.ts index b77c5ed1f..db521c8d3 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_after_final_read.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_after_final_read.ts @@ -43,7 +43,7 @@ export const fixtures: Fixture[] = [ // (p, q) = pair(x); + 'OP_2 OP_PICK OP_DUP OP_2 OP_MUL OP_SWAP OP_1ADD OP_ROT OP_DROP OP_SWAP OP_ROT OP_DROP OP_SWAP ' // x = x + 1; - + 'OP_2 OP_PICK OP_1ADD OP_3 OP_ROLL OP_DROP OP_ROT OP_ROT ' + + 'OP_ROT OP_1ADD OP_ROT OP_ROT ' // Loop condition + 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL ' // require(q == 0 || q == 2); @@ -52,13 +52,13 @@ export const fixtures: Fixture[] = [ + 'OP_NIP OP_NIP ' // } + 'OP_ENDIF', - fingerprint: 'c8619cca31b03183005fff30a7e1a4abac2b15f0dab8e28526c17aa5edbd6659', + fingerprint: '753932ad4ea2741423cb8c655ecd391aef7aeb228f53b0ef67ba3ef32dd5e52c', debug: { - bytecode: '76009c635a00785a9d5379519c6353797652957c8b7b757c7b757c6876009c7c529c9b77777767519d5a00785a9d655279519c766b6352797652957c8b7b757c7b757c52798b537a757b7b686c916676009c7c529c9b777768', - sourceMap: '11:4:19:5;;;;12:16:12:18;13::13:17;14::14;:21::23;:8::25:1;15:12:15:13:0;;:17::18;:12:::1;:20:17:9:0;16:26:16:27;;:21::28:1;;;;;:12::29;;;;;;15:20:17:9;18:16:18:17:0;:21::22;:16:::1;:26::27:0;:31::32;:26:::1;:8::34;11:29:19:5;;;:4;21::30::0;;22:16:22:18;23::23:17;24::24;:21::23;:8::25:1;25::28:9:0;:15:25:16;;:20::21;:15:::1;;;:23:28:9:0;26:26:26:27;;:21::28:1;;;;;:12::29;;;;;;27:16:27:17:0;;:::21:1;:12::22;;;;;25:23:28:9;;:8;;29:16:29:17:0;:21::22;:16:::1;:26::27:0;:31::32;:26:::1;:8::34;21:30:30:5;;10:0:31:1', + bytecode: '76009c635a00785a9d5379519c6353797652957c8b7b757c7b757c6876009c7c529c9b77777767519d5a00785a9d655279519c766b6352797652957c8b7b757c7b757c7b8b7b7b686c916676009c7c529c9b777768', + sourceMap: '11:4:19:5;;;;12:16:12:18;13::13:17;14::14;:21::23;:8::25:1;15:12:15:13:0;;:17::18;:12:::1;:20:17:9:0;16:26:16:27;;:21::28:1;;;;;:12::29;;;;;;15:20:17:9;18:16:18:17:0;:21::22;:16:::1;:26::27:0;:31::32;:26:::1;:8::34;11:29:19:5;;;:4;21::30::0;;22:16:22:18;23::23:17;24::24;:21::23;:8::25:1;25::28:9:0;:15:25:16;;:20::21;:15:::1;;;:23:28:9:0;26:26:26:27;;:21::28:1;;;;;:12::29;;;;;;27::27:22;;;;25:23:28:9;;:8;;29:16:29:17:0;:21::22;:16:::1;:26::27:0;:31::32;:26:::1;:8::34;21:30:30:5;;10:0:31:1', logs: [], - requires: [{ ip: 8, line: 14 }, { ip: 35, line: 18 }, { ip: 45, line: 24 }, { ip: 86, line: 29 }], - sourceTags: '35:37:sc;75:78:lc;86:87:sc', + requires: [{ ip: 8, line: 14 }, { ip: 35, line: 18 }, { ip: 45, line: 24 }, { ip: 82, line: 29 }], + sourceTags: '35:37:sc;71:74:lc;82:83:sc', functions: [ { name: 'pair', diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_narrowing.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_narrowing.ts index 20d1cb7b8..0e454459c 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_narrowing.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/tuple_reassignment_narrowing.ts @@ -10,7 +10,7 @@ export const fixtures: Fixture[] = [ ], bytecode: // require(data.length == 20); - 'OP_DUP OP_SIZE OP_NIP 14 OP_NUMEQUALVERIFY ' + 'OP_SIZE 14 OP_NUMEQUALVERIFY ' // data, bytes rest = other.split(20); + 'OP_SWAP 14 OP_SPLIT ' // bytes20 narrowed = data; @@ -20,16 +20,16 @@ export const fixtures: Fixture[] = [ // Cleanup + 'OP_NIP', debug: { - bytecode: '76827701149d7c01147f787b877c827700a09a77', - sourceMap: '3:16:3:20;:::27:1;;:31::33:0;:8::35:1;5:27:5:32:0;:39::41;:27::42:1;6::6:31:0;7:28:7:32;:16:::1;:36::40:0;:::47:1;;:50::51:0;:36:::1;:8::53;2:44:8:5', + bytecode: '8201149d7c01147f787b877c827700a09a77', + sourceMap: '3:16:3:27:1;:31::33:0;:8::35:1;5:27:5:32:0;:39::41;:27::42:1;6::6:31:0;7:28:7:32;:16:::1;:36::40:0;:::47:1;;:50::51:0;:36:::1;:8::53;2:44:8:5', logs: [], requires: [ - { ip: 4, line: 3 }, - { ip: 17, line: 7 }, + { ip: 2, line: 3 }, + { ip: 15, line: 7 }, ], - sourceTags: '17:17:sc', + sourceTags: '15:15:sc', }, - fingerprint: '1a51d44cecfd534a1332d599fee5890353c16bc9bcb4514ab7f46a86747640d8', + fingerprint: '74422e75c6073e2286b54220945a63fe2ff12f917d486f34e8529363ce522cf8', }, }, ]; diff --git a/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_nested.ts b/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_nested.ts index a9281c3b5..ffc68bed6 100644 --- a/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_nested.ts +++ b/packages/cashc/test/generation/fixtures/valid-contract-files/while_loop_nested.ts @@ -18,28 +18,28 @@ export const fixtures: Fixture[] = [ // while (j < 2) { + 'OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF ' // total = total + 1; - + 'OP_OVER OP_1ADD OP_ROT OP_DROP OP_SWAP ' + + 'OP_SWAP OP_1ADD OP_SWAP ' // j = j + 1; + 'OP_1ADD ' // Loop condition + 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL ' // i = i + 1; - + 'OP_2 OP_PICK OP_1ADD OP_3 OP_ROLL OP_DROP OP_ROT OP_ROT ' + + 'OP_ROT OP_1ADD ' // Cleanup - + 'OP_DROP ' + + 'OP_NIP OP_SWAP ' // Loop condition + 'OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL ' // require(i == 2); + 'OP_SWAP OP_2 OP_NUMEQUALVERIFY ' // require(total == 4); + 'OP_4 OP_NUMEQUAL', - fingerprint: '1994bca592984b848eb3fba2915fe03e0ef2fbd13e0e6e5f6efb60849cec54aa', + fingerprint: 'd50bb71e353bc7c4b2acd4d9c858760aaf37f88fa10e263dc85002bfed2bbfc2', debug: { - bytecode: '00006578529f766b63006576529f766b63788b7b757c8b686c916652798b537a757b7b75686c91667c529d549c', - sourceMap: '3:16:3:17;4:20:4:21;6:8:15:9;:15:6:16;:19::20;:15:::1;;;:22:15:9:0;7:20:7:21;9:12:12:13;:19:9:20;:23::24;:19:::1;;;:26:12:13:0;10:24:10:29;:::33:1;:16::34;;;11::11:26;9:26:12:13;;:12;;14:16:14:17:0;;:::21:1;:12::22;;;;;6:22:15:9;;;:8;;17:16:17:17:0;:21::22;:8::24:1;18:25:18:26:0;:8::28:1', + bytecode: '00006578529f766b63006576529f766b637c8b7c8b686c91667b8b777c686c91667c529d549c', + sourceMap: '3:16:3:17;4:20:4:21;6:8:15:9;:15:6:16;:19::20;:15:::1;;;:22:15:9:0;7:20:7:21;9:12:12:13;:19:9:20;:23::24;:19:::1;;;:26:12:13:0;10:16:10:34:1;;;11::11:26;9:26:12:13;;:12;;14::14:22;;6:22:15:9;;;;:8;;17:16:17:17:0;:21::22;:8::24:1;18:25:18:26:0;:8::28:1', logs: [], - requires: [{ ip: 42, line: 17 }, { ip: 45, line: 18 }], - sourceTags: '23:26:lc;35:35:sc;36:39:lc', + requires: [{ ip: 35, line: 17 }, { ip: 38, line: 18 }], + sourceTags: '21:24:lc;29:32:lc', }, }, }, diff --git a/packages/cashc/test/valid-contract-files/deep_increment_decrement.cash b/packages/cashc/test/valid-contract-files/deep_increment_decrement.cash new file mode 100644 index 000000000..3f50b75a2 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/deep_increment_decrement.cash @@ -0,0 +1,27 @@ +contract DeepIncrementDecrement() { + function loop(int budget) { + int remaining = budget; + int steps = 0; + int a = 1; + int b = 2; + int c = 3; + + for (int i = 0; i < 3; i++) { + remaining = remaining - 1; + steps = steps + 1; + } + + require(a + b + c == 6); + require(remaining + steps == budget); + } + + function branch(int x, int y, int z) { + if (x > y) { + x = x - 1; + y = y - 1; + z = z - 1; + } + + require(x + y + z > 0); + } +} diff --git a/packages/utils/src/optimisations.ts b/packages/utils/src/optimisations.ts index 32bd65567..1e5204875 100644 --- a/packages/utils/src/optimisations.ts +++ b/packages/utils/src/optimisations.ts @@ -173,4 +173,56 @@ export const optimisationReplacements = [ ['OP_SWAP OP_MIN', 'OP_MIN'], ['OP_SWAP OP_MAX', 'OP_MAX'], ['OP_SWAP OP_2DROP', 'OP_2DROP'], + + // Remove extraneous OP_DUP before OP_SIZE, which leaves its input on the stack + ['OP_DUP OP_SIZE OP_NIP', 'OP_SIZE'], + + // Replace stack shuffles with shorter equivalents + ['OP_SWAP OP_OVER', 'OP_TUCK'], + // Note that OP_3DUP also copies an item that is then dropped, which raises the operation cost when that item is large + ['OP_2 OP_PICK OP_2 OP_PICK', 'OP_3DUP OP_DROP'], + ['OP_2 OP_PICK OP_OVER', 'OP_3DUP OP_NIP'], + ['OP_2 OP_PICK OP_NIP', 'OP_DROP OP_OVER'], + ['OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK', 'OP_2SWAP OP_ROT'], + // OP_TUCK leaves the two operands in the opposite order, so this only holds for order-independent comparisons + ['OP_DUP OP_ROT OP_NUMEQUALVERIFY', 'OP_TUCK OP_NUMEQUALVERIFY'], + + // Replace copy-and-drop around in-place increments and decrements of a deeper variable (e.g. x = x + 1 inside a branch) + // These come before the deeper drop rules below, which would otherwise match the end of these patterns first + ['OP_OVER OP_1ADD OP_ROT OP_DROP', 'OP_SWAP OP_1ADD'], + ['OP_2 OP_PICK OP_1ADD OP_3 OP_ROLL OP_DROP', 'OP_ROT OP_1ADD'], + ['OP_3 OP_PICK OP_1ADD OP_4 OP_ROLL OP_DROP', 'OP_3 OP_ROLL OP_1ADD'], + ['OP_4 OP_PICK OP_1ADD OP_5 OP_ROLL OP_DROP', 'OP_4 OP_ROLL OP_1ADD'], + ['OP_5 OP_PICK OP_1ADD OP_6 OP_ROLL OP_DROP', 'OP_5 OP_ROLL OP_1ADD'], + ['OP_6 OP_PICK OP_1ADD OP_7 OP_ROLL OP_DROP', 'OP_6 OP_ROLL OP_1ADD'], + ['OP_7 OP_PICK OP_1ADD OP_8 OP_ROLL OP_DROP', 'OP_7 OP_ROLL OP_1ADD'], + ['OP_8 OP_PICK OP_1ADD OP_9 OP_ROLL OP_DROP', 'OP_8 OP_ROLL OP_1ADD'], + ['OP_9 OP_PICK OP_1ADD OP_10 OP_ROLL OP_DROP', 'OP_9 OP_ROLL OP_1ADD'], + ['OP_10 OP_PICK OP_1ADD OP_11 OP_ROLL OP_DROP', 'OP_10 OP_ROLL OP_1ADD'], + ['OP_11 OP_PICK OP_1ADD OP_12 OP_ROLL OP_DROP', 'OP_11 OP_ROLL OP_1ADD'], + ['OP_12 OP_PICK OP_1ADD OP_13 OP_ROLL OP_DROP', 'OP_12 OP_ROLL OP_1ADD'], + ['OP_13 OP_PICK OP_1ADD OP_14 OP_ROLL OP_DROP', 'OP_13 OP_ROLL OP_1ADD'], + ['OP_14 OP_PICK OP_1ADD OP_15 OP_ROLL OP_DROP', 'OP_14 OP_ROLL OP_1ADD'], + ['OP_15 OP_PICK OP_1ADD OP_16 OP_ROLL OP_DROP', 'OP_15 OP_ROLL OP_1ADD'], + ['OP_OVER OP_1SUB OP_ROT OP_DROP', 'OP_SWAP OP_1SUB'], + ['OP_2 OP_PICK OP_1SUB OP_3 OP_ROLL OP_DROP', 'OP_ROT OP_1SUB'], + ['OP_3 OP_PICK OP_1SUB OP_4 OP_ROLL OP_DROP', 'OP_3 OP_ROLL OP_1SUB'], + ['OP_4 OP_PICK OP_1SUB OP_5 OP_ROLL OP_DROP', 'OP_4 OP_ROLL OP_1SUB'], + ['OP_5 OP_PICK OP_1SUB OP_6 OP_ROLL OP_DROP', 'OP_5 OP_ROLL OP_1SUB'], + ['OP_6 OP_PICK OP_1SUB OP_7 OP_ROLL OP_DROP', 'OP_6 OP_ROLL OP_1SUB'], + ['OP_7 OP_PICK OP_1SUB OP_8 OP_ROLL OP_DROP', 'OP_7 OP_ROLL OP_1SUB'], + ['OP_8 OP_PICK OP_1SUB OP_9 OP_ROLL OP_DROP', 'OP_8 OP_ROLL OP_1SUB'], + ['OP_9 OP_PICK OP_1SUB OP_10 OP_ROLL OP_DROP', 'OP_9 OP_ROLL OP_1SUB'], + ['OP_10 OP_PICK OP_1SUB OP_11 OP_ROLL OP_DROP', 'OP_10 OP_ROLL OP_1SUB'], + ['OP_11 OP_PICK OP_1SUB OP_12 OP_ROLL OP_DROP', 'OP_11 OP_ROLL OP_1SUB'], + ['OP_12 OP_PICK OP_1SUB OP_13 OP_ROLL OP_DROP', 'OP_12 OP_ROLL OP_1SUB'], + ['OP_13 OP_PICK OP_1SUB OP_14 OP_ROLL OP_DROP', 'OP_13 OP_ROLL OP_1SUB'], + ['OP_14 OP_PICK OP_1SUB OP_15 OP_ROLL OP_DROP', 'OP_14 OP_ROLL OP_1SUB'], + ['OP_15 OP_PICK OP_1SUB OP_16 OP_ROLL OP_DROP', 'OP_15 OP_ROLL OP_1SUB'], + + // Replace drops of items deeper in the stack (e.g. from scope cleanup) with shorter equivalents + ['OP_ROT OP_DROP OP_NIP', 'OP_NIP OP_NIP'], + ['OP_3 OP_ROLL OP_DROP OP_ROT', 'OP_2SWAP OP_NIP'], + ['OP_ROT OP_ROT OP_DROP', 'OP_NIP OP_SWAP'], + ['OP_3 OP_ROLL OP_DROP OP_NIP OP_NIP', 'OP_NIP OP_NIP OP_NIP'], ] as [string, string][]; diff --git a/packages/utils/test/fixtures/bitauth-script.fixture.ts b/packages/utils/test/fixtures/bitauth-script.fixture.ts index 5dcce2670..1fe3eab02 100644 --- a/packages/utils/test/fixtures/bitauth-script.fixture.ts +++ b/packages/utils/test/fixtures/bitauth-script.fixture.ts @@ -231,31 +231,31 @@ contract ForWhileNested() { } } `.replace(/^\n+/, '').replace(/\n+$/, ''), - asmBytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_ROT OP_ROT OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_4 OP_NUMEQUAL', - sourceMap: '3:18:3:19;5:21:5:22;:8:13:9;:24:5:25;:28::29;:24:::1;;;:42:13:9:0;6:20:6:21;8:12:12:13;:19:8:20;:23::24;:19:::1;;;:26:12:13:0;9:22:9:25;;:28::29;;:22:::1;:32::33:0;:22:::1;:16::34;;;;;10::10:26;8:26:12:13;;:12;;5:35:5:36:0;:::40:1;:31;;::13:9;:42;;:8;;;15:23:15:24:0;:8::26:1', - sourceTags: '30:33:lc;34:38:fu;39:42:lc;43:43:sc', + asmBytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_3DUP OP_DROP OP_ADD OP_OVER OP_ADD OP_2SWAP OP_NIP OP_ROT OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_SWAP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_4 OP_NUMEQUAL', + sourceMap: '3:18:3:19;5:21:5:22;:8:13:9;:24:5:25;:28::29;:24:::1;;;:42:13:9:0;6:20:6:21;8:12:12:13;:19:8:20;:23::24;:19:::1;;;:26:12:13:0;9:22:9:29;;::::1;:32::33:0;:22:::1;:16::34;;;10::10:26;8:26:12:13;;:12;;5:31:5:40;;::13:9;:42;;:8;;;15:23:15:24:0;:8::26:1', + sourceTags: '26:29:lc;30:32:fu;33:36:lc;37:37:sc', expectedBitAuthScript: ` - /* contract ForWhileNested() { */ - /* function spend() { */ -OP_0 /* int sum = 0; */ - /* */ -OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF /* for (int i = 0; i < 2; i = i + 1) { */ -OP_0 /* int j = 0; */ - /* */ -OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF /* while (j < 2) { */ -OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_ROT OP_ROT /* sum = sum + i + j; */ -OP_1ADD /* j = j + 1; */ - /* console.log("sum:", sum, "i:", i, "j:", j); */ -OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ - /* } */ -OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP /* >>> for-loop update (i = i + 1) */ -OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ -OP_DROP /* >>> scope cleanup */ - /* } */ - /* */ -OP_4 OP_NUMEQUAL /* require(sum == 4); */ - /* } */ - /* } */ + /* contract ForWhileNested() { */ + /* function spend() { */ +OP_0 /* int sum = 0; */ + /* */ +OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF /* for (int i = 0; i < 2; i = i + 1) { */ +OP_0 /* int j = 0; */ + /* */ +OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF /* while (j < 2) { */ +OP_3DUP OP_DROP OP_ADD OP_OVER OP_ADD OP_2SWAP OP_NIP OP_ROT /* sum = sum + i + j; */ +OP_1ADD /* j = j + 1; */ + /* console.log("sum:", sum, "i:", i, "j:", j); */ +OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ + /* } */ +OP_SWAP OP_1ADD OP_NIP /* >>> for-loop update (i = i + 1) */ +OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ +OP_DROP /* >>> scope cleanup */ + /* } */ + /* */ +OP_4 OP_NUMEQUAL /* require(sum == 4); */ + /* } */ + /* } */ `.replace(/^\n+/, '').replace(/\n+$/, ''), }, { @@ -279,35 +279,35 @@ OP_4 OP_NUMEQUAL require(total + count == 12); } }`, - asmBytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_ROT OP_ROT OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_1ADD OP_DUP OP_3 OP_GREATERTHANOREQUAL OP_UNTIL OP_ADD OP_12 OP_NUMEQUAL', - sourceMap: '3:20:3:21;5:21:5:22;:8:10:9;:24:5:25;:28::29;:24:::1;;;6:19:10:9:0;7:25:7:26;:12:9:13;:28:7:29;:32::33;:28:::1;;;:46:9:13:0;8:24:8:29;;:32::33;;:24:::1;:36::37:0;:24:::1;:16::38;;;;;7:35:7:44;:46:9:13;;:12;;;5:31:6:17;6:19:10:9;;5:8;;;12:20:12:21:0;13:8:15:28;14:12:14:30:1;15:17:15:22:0;:25::26;13:8::28:1;;17:16:17:29;:33::35:0;:8::37:1', - sourceTags: '29:29:fu;30:33:lc;34:34:sc;35:35:fu;36:39:lc;40:40:sc', + asmBytecode: 'OP_0 OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF OP_3DUP OP_DROP OP_ADD OP_OVER OP_ADD OP_2SWAP OP_NIP OP_ROT OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_1ADD OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_1ADD OP_DUP OP_3 OP_GREATERTHANOREQUAL OP_UNTIL OP_ADD OP_12 OP_NUMEQUAL', + sourceMap: '3:20:3:21;5:21:5:22;:8:10:9;:24:5:25;:28::29;:24:::1;;;6:19:10:9:0;7:25:7:26;:12:9:13;:28:7:29;:32::33;:28:::1;;;:46:9:13:0;8:24:8:33;;::::1;:36::37:0;:24:::1;:16::38;;;7:35:7:44;:46:9:13;;:12;;;5:31:6:17;6:19:10:9;;5:8;;;12:20:12:21:0;13:8:15:28;14:12:14:30:1;15:17:15:22:0;:25::26;13:8::28:1;;17:16:17:29;:33::35:0;:8::37:1', + sourceTags: '25:25:fu;26:29:lc;30:30:sc;31:31:fu;32:35:lc;36:36:sc', expectedBitAuthScript: ` - /* contract NestedForWithDoWhile() { */ - /* function spend() { */ -OP_0 /* int total = 0; */ - /* */ -OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK /* for (int i = 0; i < 3; i = */ -OP_IF /* i + 1) { */ -OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF /* for (int j = 0; j < 2; j = j + 1) { */ -OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_ROT OP_ROT /* total = total + i + j; */ -OP_1ADD /* >>> for-loop update (j = j + 1) */ -OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ -OP_DROP /* >>> scope cleanup */ - /* } */ -OP_1ADD /* >>> for-loop update () */ -OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ -OP_DROP /* >>> scope cleanup */ - /* } */ - /* */ -OP_0 /* int count = 0; */ -OP_BEGIN /* do { */ -OP_1ADD /* count = count + 1; */ -OP_DUP OP_3 OP_GREATERTHANOREQUAL OP_UNTIL /* } while (count < 3); */ - /* */ -OP_ADD OP_12 OP_NUMEQUAL /* require(total + count == 12); */ - /* } */ - /* } */ + /* contract NestedForWithDoWhile() { */ + /* function spend() { */ +OP_0 /* int total = 0; */ + /* */ +OP_0 OP_BEGIN OP_DUP OP_3 OP_LESSTHAN OP_DUP OP_TOALTSTACK /* for (int i = 0; i < 3; i = */ +OP_IF /* i + 1) { */ +OP_0 OP_BEGIN OP_DUP OP_2 OP_LESSTHAN OP_DUP OP_TOALTSTACK OP_IF /* for (int j = 0; j < 2; j = j + 1) { */ +OP_3DUP OP_DROP OP_ADD OP_OVER OP_ADD OP_2SWAP OP_NIP OP_ROT /* total = total + i + j; */ +OP_1ADD /* >>> for-loop update (j = j + 1) */ +OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ +OP_DROP /* >>> scope cleanup */ + /* } */ +OP_1ADD /* >>> for-loop update () */ +OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL /* >>> loop condition check */ +OP_DROP /* >>> scope cleanup */ + /* } */ + /* */ +OP_0 /* int count = 0; */ +OP_BEGIN /* do { */ +OP_1ADD /* count = count + 1; */ +OP_DUP OP_3 OP_GREATERTHANOREQUAL OP_UNTIL /* } while (count < 3); */ + /* */ +OP_ADD OP_12 OP_NUMEQUAL /* require(total + count == 12); */ + /* } */ + /* } */ `.replace(/^\n+/, '').replace(/\n+$/, ''), }, { diff --git a/website/docs/releases/release-notes.md b/website/docs/releases/release-notes.md index cb7dd3c95..8a6b22391 100644 --- a/website/docs/releases/release-notes.md +++ b/website/docs/releases/release-notes.md @@ -20,6 +20,7 @@ This release contains several breaking changes, please refer to the [migration n - :bug: Fix bug where hex literals with an odd number of digits (e.g. `0x123`) compiled to a different value (`0x1203`), they now cause a compile error. - :racehorse: Add new `OP_SWAP OP_MUL`, `OP_NOT OP_NOT` and `OP_TOALTSTACK OP_FROMALTSTACK` optimisations. - :racehorse: Add new optimisations for loop counter updates, reassignments inside loops and order-independent operations (`OP_BOOLAND`, `OP_BOOLOR`, `OP_MIN`, `OP_MAX`). +- :racehorse: Add new optimisations for increments and decrements of variables deeper in the stack, scope cleanup, stack shuffles (using `OP_TUCK`, `OP_3DUP` and `OP_2SWAP`) and `OP_SIZE` on a duplicated value. - :racehorse: Greatly improve compiler speed for very large contracts. #### CashScript SDK From e6aab31856a94424d838830211aac6d05d8157dd Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 29 Sep 2026 10:46:29 +0200 Subject: [PATCH 2/3] Add note on dynamic optimisations for v0.15 Added a note about the need for dynamic optimisations in future versions. --- packages/utils/src/optimisations.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/utils/src/optimisations.ts b/packages/utils/src/optimisations.ts index 1e5204875..729d52d32 100644 --- a/packages/utils/src/optimisations.ts +++ b/packages/utils/src/optimisations.ts @@ -184,6 +184,10 @@ export const optimisationReplacements = [ ['OP_2 OP_PICK OP_OVER', 'OP_3DUP OP_NIP'], ['OP_2 OP_PICK OP_NIP', 'OP_DROP OP_OVER'], ['OP_TOALTSTACK OP_ROT OP_ROT OP_FROMALTSTACK', 'OP_2SWAP OP_ROT'], + + // NOTE: these optimisations (as well as some others above) are currently hardcoded, but should become dynamic. + // This is something we want to properly implement in v0.15 + // OP_TUCK leaves the two operands in the opposite order, so this only holds for order-independent comparisons ['OP_DUP OP_ROT OP_NUMEQUALVERIFY', 'OP_TUCK OP_NUMEQUALVERIFY'], @@ -219,6 +223,7 @@ export const optimisationReplacements = [ ['OP_13 OP_PICK OP_1SUB OP_14 OP_ROLL OP_DROP', 'OP_13 OP_ROLL OP_1SUB'], ['OP_14 OP_PICK OP_1SUB OP_15 OP_ROLL OP_DROP', 'OP_14 OP_ROLL OP_1SUB'], ['OP_15 OP_PICK OP_1SUB OP_16 OP_ROLL OP_DROP', 'OP_15 OP_ROLL OP_1SUB'], + ///////////////////////////////////////////////////////////////////////// // Replace drops of items deeper in the stack (e.g. from scope cleanup) with shorter equivalents ['OP_ROT OP_DROP OP_NIP', 'OP_NIP OP_NIP'], From cddbb9bf6db69e2b3d9a8d9cbefb21d2b62c1d70 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 29 Sep 2026 10:59:41 +0200 Subject: [PATCH 3/3] Update release notes with bug fixes and optimizations Consolidate multiple optimization entries and fix various bugs related to literals and bytecode. --- website/docs/releases/release-notes.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/website/docs/releases/release-notes.md b/website/docs/releases/release-notes.md index 8a6b22391..c07eaa741 100644 --- a/website/docs/releases/release-notes.md +++ b/website/docs/releases/release-notes.md @@ -18,9 +18,7 @@ This release contains several breaking changes, please refer to the [migration n - :bug: Fix bug where date literal parsing was different per locale, it now uses UTC. - :bug: Fix bug where `LockingBytecodeNullData` used an incorrect push opcode for empty chunks and chunks of 128-255 bytes, so it did not match the SDK's `addOpReturnOutput()`. Chunks with a value or length that is known at compile time now use a precomputed push opcode, which makes them smaller. This changes the bytecode of all contracts that use `LockingBytecodeNullData`. - :bug: Fix bug where hex literals with an odd number of digits (e.g. `0x123`) compiled to a different value (`0x1203`), they now cause a compile error. -- :racehorse: Add new `OP_SWAP OP_MUL`, `OP_NOT OP_NOT` and `OP_TOALTSTACK OP_FROMALTSTACK` optimisations. -- :racehorse: Add new optimisations for loop counter updates, reassignments inside loops and order-independent operations (`OP_BOOLAND`, `OP_BOOLOR`, `OP_MIN`, `OP_MAX`). -- :racehorse: Add new optimisations for increments and decrements of variables deeper in the stack, scope cleanup, stack shuffles (using `OP_TUCK`, `OP_3DUP` and `OP_2SWAP`) and `OP_SIZE` on a duplicated value. +- :racehorse: Add new optimisations for loop counter updates, reassignments, scope cleanup, stack shuffles, and order-independent operations. - :racehorse: Greatly improve compiler speed for very large contracts. #### CashScript SDK