diff --git a/flipjump/stl/hex/pointers/stack.fj b/flipjump/stl/hex/pointers/stack.fj index 7a1de16..c7345cc 100644 --- a/flipjump/stl/hex/pointers/stack.fj +++ b/flipjump/stl/hex/pointers/stack.fj @@ -30,10 +30,11 @@ ns hex { .ptr_sub hex.pointers.sp, value } - // Time Complexity: w(1.5@+5) + 9@+14 - // Space Complexity: w(1.875@+20) + 5@+55 + // Time Complexity: w(2.25@+10) + 9@+ 51 + // Space Complexity: w(2.62@+49) + 20@+231 // Like: stack[++sp] = return_address - // Pushes the given return_address to the next cell in the stack (assumes it's zero). Increments sp. + // Pushes the given return_address to the next cell in the stack. Increments sp. + // Note: the pushed returned address must be popd out afterwards with pop_ret_address. // return_address is a fj-op address, so we assume is dw-aligned. def push_ret_address return_address < hex.pointers.sp { .sp_inc @@ -41,7 +42,7 @@ ns hex { .ptr_wflip_2nd_word hex.pointers.sp, return_address } - // Time Complexity: w(1.5@+5) + 9@+23 + // Time Complexity: w( 1.5@+ 5) + 9@+23 // Space Complexity: w(1.875@+20) + 5@+67 // Like: stack[sp--] = 0 // Pops the given return_address from the current cell in the stack (assumes it has the value of the return_address). Decrements sp. @@ -51,64 +52,77 @@ ns hex { .sp_dec } - - // Time Complexity: w(0.5@+2) + 14@+26 - // Space Complexity: w(0.875@+5.25) + 10@+131 - // Like: stack[++sp] = hex (assumes stack[++sp] == 0 beforehand, which the stack-macros guarantee). - // Pushes the given hex to the next cell in the stack (assumes it's zero). Increments sp. + // Time Complexity: w(0.75@+ 5) + 20@+ 39 + // Space Complexity: w(1.13@+32) + 16@+167 + // Like: stack[++sp] = hex + // Pushes the given hex to the next cell in the stack. Increments sp. def push_hex hex < hex.pointers.sp { .sp_inc .write_hex hex.pointers.sp, hex } - // Time Complexity: n(w(0.5@+2) + 14@+26) - // Space Complexity: n(w(0.875@+5.25) + 10@+131) - // Like: stack[sp+1:][:n] = hex[:n]; sp += n (assumes stack[sp:sp+n] == 0 beforehand, which the stack-macros guarantee). - // Pushes the given hex[:n] to the next n cells in the stack (assumes they're zero). Increments sp by n. - def push n, hex { - rep(n, i) .push_hex hex+i*dw - } - - - // Time Complexity: w(0.5@+2) + 14@+35 - // Space Complexity: w(0.875@+17.25) + 10@+143 - // Like: stack[sp--] = 0 (assumes stack[sp] == unchanged_hex beforehand) - // Pops the given unchanged_hex (a hex) from the current cell in the stack (assumes it has the exact value of hex). Decrements sp. - def pop_unchanged_parameter unchanged_hex < hex.pointers.sp { - .xor_hex_to_ptr hex.pointers.sp, unchanged_hex - .sp_dec + // Time Complexity: w(0.75@+ 5) + 26@+ 51 + // Space Complexity: w(1.13@+32) + 22@+255 + // Like: stack[++sp] = byte[:2] + // Pushes the given byte to the next cell in the stack. Increments sp. + // byte is a hex[:2]. + def push_byte byte < hex.pointers.sp { + .sp_inc + .write_byte hex.pointers.sp, byte } - // Time Complexity: n(w(0.5@+2) + 14@+35) - // Space Complexity: n(w(0.875@+17.25) + 10@+143) - // Like: sp -= n; stack[sp+1:][:n] = 0 (assumes stack[sp+1-n:][:n] == unchanged_hex[:n] beforehand) - // Pops the given unchanged_hex (a hex[:n]) from the current cell in the stack (assumes it has the exact value of hex). Decrements sp by n. - def pop_unchanged_parameter n, unchanged_hex { - rep(n, i) .pop_unchanged_parameter unchanged_hex+i*dw + // Time Complexity: n(w(0.38@+ 3) + 13@+ 25) + // Space Complexity: n(w(0.56@+16) + 11@+128) + // Like: stack[sp+1:][:M] = hex[:n]; sp += n. + // Pushes the given hex[:n] to the next M cells in the stack. Increments sp by M. + // M is (n+1)/2, which is because the function pushes the entire parameter as bytes. + // + // Note: This macro pushes bytes to the stack (2 hexs in one stack-cell). + // + // Note: The push/pop usage must be coordinated, and every push X must be untangled by a pop X (or a sp_sub (X+1)/2) + // For example: "push 3; push 3" can't be read with a "pop 6". That's because each "push 3" takes 2 stack cells, + // while the "pop 6" only pops out 3 stack cells (so there is another cell that needs to be popped. + def push n, hex { + rep(n/2, i) .push_byte hex+2*i*dw + rep(n%2, i) .push_hex hex+(n-1)*dw } - - // Time Complexity: w(1.25@+7) + 21@+48 - // Space Complexity: w(1.625@+46) + 17@+191 - // Like: hex = stack[sp] - // stack[sp--] = 0 + // Time Complexity: w(0.75@+ 5) + 16@+ 36 + // Space Complexity: w(1.13@+32) + 12@+115 + // Like: hex = stack[sp--] // Pops the current stack cell (only the least-significant-hex of it) into the the given hex. Decrements sp. - // hex is only an output parameter + // hex is only an output parameter. def pop_hex hex < hex.pointers.sp { - hex.zero hex - .xor_hex_from_ptr hex, hex.pointers.sp -// .pop_unchanged_parameter hex + .read_hex hex, hex.pointers.sp + .sp_dec + } + + // Time Complexity: w(0.75@+ 5) + 18@+ 36 + // Space Complexity: w(1.13@+32) + 14@+139 + // Like: byte[:2] = stack[sp--] + // Pops the current stack cell into the the given byte. Decrements sp. + // byte is hex[:2], and it's only an output parameter. + def pop_byte byte < hex.pointers.sp { + .read_byte byte, hex.pointers.sp .sp_dec } - // Time Complexity: n(w(1.25@+7) + 21@+48) - // Space Complexity: n(w(1.625@+46) + 17@+191) - // Like: sp -= n - // hex[:n] = stack[sp+1][:n] - // stack[sp+1:][:n] = 0 - // Pops the current stack cell into the the given hex[:n]. Decrements sp by n. + // Time Complexity: n(w(0.38@+ 3) + 9@+18) + // Space Complexity: n(w(0.56@+16) + 7@+70) + // Like: sp -= M + // hex[:n] = stack[sp+1:][:M] + // Pops multiple stack cell into the the given hex[:n]. Decrements sp by M. + // M is (n+1)/2, which is because the function pops all the parameters as bytes. + // + // Note: The pops assume that they were pushed as bytes to the stack (2 hexs in one stack-cell). + // So this macro pops bytes. + // + // Note: The push/pop usage must be coordinated, and every push X must be untangled by a pop X (or a sp_sub (X+1)/2) + // For example: "push 3; push 3" can't be read with a "pop 6". That's because each "push 3" takes 2 stack cells, + // while the "pop 6" only pops out 3 stack cells (so there is another cell that needs to be popped. // hex[:n] is only an output parameter. def pop n, hex { - rep(n, i) .pop_hex hex+(n-1-i)*dw + rep(n%2, i) .pop_hex hex+(n-1)*dw + rep(n/2, i) .pop_byte hex+(n-n%2-2*(i+1))*dw } } diff --git a/flipjump/stl/ptrlib.fj b/flipjump/stl/ptrlib.fj index 55b9d1e..37f1598 100644 --- a/flipjump/stl/ptrlib.fj +++ b/flipjump/stl/ptrlib.fj @@ -24,8 +24,8 @@ ns stl { // @requires the stack_init. ns stl { - // Time Complexity: ~1.5@ (exact: w(1.5@+5) + 9@+15) - // Space Complexity: <2@ (exact: w(1.875@+20) + 5@+55) + // Time Complexity: ~2.5w@ (exact: w(2.25@+10) + 24@+51) + // Space Complexity: <3w@ (exact: w(2.625@+49) + 20@+231) // Saves the return address to the stack and jumps to the given "address". // When returned, it removes the return-address from the stack. // @@ -40,21 +40,21 @@ ns stl { hex.pop_ret_address return_label } - // Time Complexity: <2@ (exact: w(1.5@+5) + 22@+20) - // Space Complexity: <2@ (exact: w(1.875@+20) + 28@+75) + // Time Complexity: <3w@ (exact: w(2.25@+10) + 37@+57) + // Space Complexity: <3w@ (exact: w(2.625@+49) + 43@+251) // Saves the return address to the stack and jumps to the given "address". - // When returned, it removes the value from the stack, and pops "params_hex_length" variables from the stack. + // When returned, it removes the return-address from the stack, and pops "params_stack_length" cells from the stack. // // note: the pop_ret_address is for the future return (counts as space, but not time, complexity). // @requires the stack_init. - def call address, params_hex_length @ return_label { + def call address, params_stack_length @ return_label { hex.push_ret_address return_label ;address pad 2 return_label: hex.pop_ret_address return_label - hex.sp_sub params_hex_length + hex.sp_sub params_stack_length } diff --git a/programs/func_tests/func3.fj b/programs/func_tests/func3.fj index 0eacd21..2e6350a 100644 --- a/programs/func_tests/func3.fj +++ b/programs/func_tests/func3.fj @@ -1,4 +1,4 @@ -// Tests push/pop_unchanged_parameter with an empty function call +// Tests push_hex/pop_hex (unchanged hex) with an empty function call stl.startup_and_init_all 10 @@ -11,7 +11,7 @@ test3: stl.output 'B' stl.call func3 stl.output 'D' - hex.pop_unchanged_parameter x3 + hex.pop_hex x3 stl.output 'E' stl.output '\n' diff --git a/programs/func_tests/func5.fj b/programs/func_tests/func5.fj index 9bcb879..0544598 100644 --- a/programs/func_tests/func5.fj +++ b/programs/func_tests/func5.fj @@ -18,9 +18,9 @@ test5: stl.output ' ' stl.output 'A' - hex.pop_unchanged_parameter y5 + hex.pop_hex y5 stl.output 'B' - hex.pop_unchanged_parameter x5 + hex.pop_hex x5 stl.output 'C' hex.pop_hex res5 stl.output 'D' diff --git a/programs/func_tests/func6.fj b/programs/func_tests/func6.fj index 81e7152..86fa7e0 100644 --- a/programs/func_tests/func6.fj +++ b/programs/func_tests/func6.fj @@ -2,6 +2,7 @@ N = 4 +STACK_N = (N+1)/2 stl.startup_and_init_all 100 hex.set N, x, 7438 @@ -10,7 +11,7 @@ hex.set N, y, 2524 hex.push N, x hex.push N, y -stl.call math.add, N +stl.call math.add, STACK_N hex.pop N, res hex.print_as_digit N, res, 1 stl.output '\n' @@ -28,7 +29,7 @@ add: hex.add N, .res, .temp hex.push N, .res - hex.sp_add N+1 + hex.sp_add STACK_N+1 stl.return diff --git a/programs/func_tests/func7.fj b/programs/func_tests/func7.fj index f50d1b1..ce3b643 100644 --- a/programs/func_tests/func7.fj +++ b/programs/func_tests/func7.fj @@ -4,6 +4,7 @@ stl.startup_and_init_all 100 N = 2 +STACK_N = (N+1)/2 hex.set N, p1_x, 9 hex.set N, p1_y, 0-54 @@ -32,16 +33,16 @@ p3_y: hex.vec N def calc_dist_squared x1, y1, x2, y2, prefix_distance_string @ end, res < math.square_distance { stl.output 'A' - hex.push N, x1 - hex.push N, y1 - hex.push N, x2 - hex.push N, y2 + pushN x1 + pushN y1 + pushN x2 + pushN y2 stl.output 'B' - stl.call math.square_distance, 2*N + stl.call math.square_distance, 2*STACK_N stl.output 'I' - hex.pop 2*N, res + pop2N res stl.output "J\n" stl.output prefix_distance_string @@ -59,15 +60,15 @@ ns math { sub: ns sub { hex.sp_dec - hex.pop N, .res - hex.pop N, .temp + popN .res + popN .temp stl.output "-1" hex.sub N, .res, .temp stl.output '2' - hex.push N, .res - hex.sp_add N+1 + pushN .res + hex.sp_add STACK_N+1 stl.output "3-" stl.return @@ -79,15 +80,15 @@ temp: hex.vec N add_2N: ns add_2N { hex.sp_dec - hex.pop 2*N, .res - hex.pop 2*N, .temp + pop2N .res + pop2N .temp stl.output "-1" hex.add 2*N, .res, .temp stl.output '2' - hex.push 2*N, .res - hex.sp_add 2*N+1 + push2N .res + hex.sp_add 2*STACK_N+1 stl.output "3-" stl.return @@ -102,17 +103,17 @@ ns mul { hex.sp_dec stl.output '2' - hex.pop N, .temp1 + popN .temp1 hex.sign_extend 2*N, N, .temp1 stl.output '3' - hex.pop N, .temp2 + popN .temp2 hex.sign_extend 2*N, N, .temp2 stl.output '4' hex.mul 2*N, .res, .temp1, .temp2 stl.output '5' - hex.push 2*N, .res + push2N .res hex.sp_inc stl.output "6-" @@ -127,25 +128,25 @@ res: hex.vec 2*N square_sub: ns square_sub { hex.sp_dec - hex.pop N, .b - hex.pop N, .a - hex.sp_add 2*N+1 + popN .b + popN .a + hex.sp_add 2*STACK_N+1 stl.output 'a' - hex.push N, .a - hex.push N, .b - stl.call ..sub, N - hex.pop N, .res + pushN .a + pushN .b + stl.call ..sub, STACK_N + popN .res stl.output 'b' - hex.push N, .res - hex.push N, .res + pushN .res + pushN .res stl.call ..mul - hex.pop 2*N, .res + pop2N .res stl.output 'c' - hex.sp_sub 2*N+1 - hex.push 2*N, .res + hex.sp_sub 2*STACK_N+1 + push2N .res hex.sp_inc stl.output "d\n" @@ -161,36 +162,36 @@ square_distance: ns square_distance { stl.output 'C' hex.sp_dec - hex.pop N, .y2 - hex.pop N, .x2 - hex.pop N, .y1 - hex.pop N, .x1 - hex.sp_add 4*N+1 + popN .y2 + popN .x2 + popN .y1 + popN .x1 + hex.sp_add 4*STACK_N+1 stl.output 'D' // (x1-x2)**2 - hex.push N, .x1 - hex.push N, .x2 + pushN .x1 + pushN .x2 stl.call ..square_sub - hex.pop 2*N, .res1 + pop2N .res1 stl.output 'E' // (y1-y2)**2 - hex.push N, .y1 - hex.push N, .y2 + pushN .y1 + pushN .y2 stl.call ..square_sub - hex.pop 2*N, .res2 + pop2N .res2 stl.output 'F' // (x1-x2)**2 + (y1-y2)**2 - hex.push 2*N, .res1 - hex.push 2*N, .res2 - stl.call ..add_2N, 2*N - hex.pop 2*N, .res1 + push2N .res1 + push2N .res2 + stl.call ..add_2N, 2*STACK_N + pop2N .res1 stl.output 'G' - hex.sp_sub 4*N+1 - hex.push 2*N, .res1 - hex.sp_add 2*N+1 + hex.sp_sub 4*STACK_N+1 + push2N .res1 + hex.sp_add 2*STACK_N+1 stl.output 'H' stl.return @@ -203,3 +204,46 @@ res1: hex.vec 2*N res2: hex.vec 2*N } } + + +// Push+Pop fast-functions + +def pushN variable < pushN, stack_variable, pushed_ret { + hex.mov N, stack_variable, variable + stl.fcall pushN, pushed_ret +} + +def push2N variable < push2N, stack_variable, pushed_ret { + hex.mov 2*N, stack_variable, variable + stl.fcall push2N, pushed_ret +} + +def popN variable < popN, stack_variable, pushed_ret { + stl.fcall popN, pushed_ret + hex.mov N, variable, stack_variable +} + +def pop2N variable < pop2N, stack_variable, pushed_ret { + stl.fcall pop2N, pushed_ret + hex.mov 2*N, variable, stack_variable +} + + +stack_variable: hex.vec 2*N +pushed_ret: bit.bit + +pushN: +hex.push N, stack_variable +stl.fret pushed_ret + +push2N: +hex.push 2*N, stack_variable +stl.fret pushed_ret + +popN: +hex.pop N, stack_variable +stl.fret pushed_ret + +pop2N: +hex.pop 2*N, stack_variable +stl.fret pushed_ret diff --git a/pyproject.toml b/pyproject.toml index e78dd7b..84b8f23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "flipjump" -version = "1.2.0" +version = "1.2.1" description = "The single instruction language - Flip a bit, then Jump" authors = ["Tom Herman "] license = "BSD-2-Clause-Simplified"