diff --git a/benchmarks/core/dot-product.bril b/benchmarks/core/dot-product.bril new file mode 100644 index 000000000..0f044b989 --- /dev/null +++ b/benchmarks/core/dot-product.bril @@ -0,0 +1,65 @@ +@dot_product(vectorA: ptr, vectorB: ptr, size: int): int { + one: int = const 1; + index: int = const 0; + answer: int = const 0; +.loop: + ptrA: ptr = ptradd vectorA index; + ptrB: ptr = ptradd vectorB index; + valA: int = load ptrA; + valB: int = load ptrB; + tmp: int = mul valA valB; + answer: int = add answer tmp; + index: int = add index one; + cond: bool = lt index size; + br cond .loop .done; +.done: + ret answer; +} + +@main { + a: int = const 25; + b: int = const 50; + c: int = const 100; + d: int = const 150; + e: int = const 250; + f: int = const 2; + g: int = const 10; + h: int = const 20; + i: int = const 30; + j: int = const 40; + one: int = const 1; + zero: int = const 0; + size: int = const 5; + + # Create and fill vectorA + vectorA: ptr = alloc size; + indexPtr: ptr = ptradd vectorA zero; + store indexPtr a; + indexPtr: ptr = ptradd indexPtr one; + store indexPtr b; + indexPtr: ptr = ptradd indexPtr one; + store indexPtr c; + indexPtr: ptr = ptradd indexPtr one; + store indexPtr d; + indexPtr: ptr = ptradd indexPtr one; + store indexPtr e; + + # Create and fill vectorB + vectorB: ptr = alloc size; + indexPtr: ptr = ptradd vectorB zero; + store indexPtr f; + indexPtr: ptr = ptradd indexPtr one; + store indexPtr g; + indexPtr: ptr = ptradd indexPtr one; + store indexPtr h; + indexPtr: ptr = ptradd indexPtr one; + store indexPtr i; + indexPtr: ptr = ptradd indexPtr one; + store indexPtr j; + + val: int = call @dot_product vectorA vectorB size; + print val; + + free vectorA; + free vectorB; +} diff --git a/benchmarks/core/dot-product.out b/benchmarks/core/dot-product.out new file mode 100644 index 000000000..038b87558 --- /dev/null +++ b/benchmarks/core/dot-product.out @@ -0,0 +1 @@ +17050 diff --git a/benchmarks/core/dot-product.prof b/benchmarks/core/dot-product.prof new file mode 100644 index 000000000..1e1f19cef --- /dev/null +++ b/benchmarks/core/dot-product.prof @@ -0,0 +1 @@ +total_dyn_inst: 88 diff --git a/benchmarks/core/mod_inv.bril b/benchmarks/core/mod_inv.bril new file mode 100644 index 000000000..f5ee57cf6 --- /dev/null +++ b/benchmarks/core/mod_inv.bril @@ -0,0 +1,90 @@ +# ARGS: 46, 10007 +@main(n: int, p: int) { + v0: int = const 2; + two: int = id v0; + v1: int = id p; + v2: int = id two; + v3: int = sub v1 v2; + m: int = id v3; + v4: int = const 1; + ans: int = id v4; + v5: int = id n; + a: int = id v5; + v7: int = const 1; + i: int = id v7; +.for.cond.6: + v8: int = id m; + v9: int = const 0; + v10: bool = gt v8 v9; + br v10 .for.body.6 .for.end.6; +.for.body.6: + v12: int = id m; + v13: int = id m; + v14: int = id two; + v15: int = div v13 v14; + v16: int = id two; + v17: int = mul v15 v16; + v18: bool = eq v12 v17; + br v18 .then.11 .else.11; +.then.11: + jmp .endif.11; +.else.11: + v19: int = id ans; + v20: int = id a; + v21: int = mul v19 v20; + v22: int = id p; + v23: int = call @mod v21 v22; + ans: int = id v23; +.endif.11: + v24: int = id a; + v25: int = id a; + v26: int = mul v24 v25; + v27: int = id p; + v28: int = call @mod v26 v27; + a: int = id v28; + v29: int = id m; + v30: int = id two; + v31: int = div v29 v30; + m: int = id v31; + jmp .for.cond.6; +.for.end.6: + v32: int = id ans; + print v32; + v33: int = const 0; +} +@mod(n: int, p: int): int { + v0: int = id n; + v1: int = id n; + v2: int = id p; + v3: int = div v1 v2; + v4: int = id p; + v5: int = mul v3 v4; + v6: int = sub v0 v5; + ret v6; +} + +# ts2bril inv.ts | bril2txt | tail +3 | sed -E 's/float/int/g' | sed -E 's/fgt/gt/g' > inv.bril +# generated by typescript code below: +# +# function main(n: bigint, p: bigint) { +# var two: bigint = 2; +# var m: bigint = p - two; +# var ans: bigint = 1; +# var a: bigint = n; +# for (let i = 1; m > 0; m = m / two) { +# if (m == m / two * two) { +# } +# else { +# ans = mod(ans * a, p); +# } +# a = mod(a * a, p); +# } +# console.log(ans); +# } +# +# function mod(n: bigint, p: bigint): bigint { +# return n - n / p * p; +# } +# + + diff --git a/benchmarks/core/mod_inv.out b/benchmarks/core/mod_inv.out new file mode 100644 index 000000000..6726ecf8d --- /dev/null +++ b/benchmarks/core/mod_inv.out @@ -0,0 +1 @@ +2393 diff --git a/benchmarks/core/mod_inv.prof b/benchmarks/core/mod_inv.prof new file mode 100644 index 000000000..d62b26752 --- /dev/null +++ b/benchmarks/core/mod_inv.prof @@ -0,0 +1 @@ +total_dyn_inst: 558 diff --git a/benchmarks/core/palindrome.bril b/benchmarks/core/palindrome.bril new file mode 100644 index 000000000..bbfcbdcb3 --- /dev/null +++ b/benchmarks/core/palindrome.bril @@ -0,0 +1,80 @@ +# ARGS: 12321 +@main(in: int) { +#in: int = const 2343553432; +ten: int = const 10; +zero: int = const 0; +one: int = const 1; +index: int = const 1; +not_finished: bool = const true; +.for.cond: + br not_finished .for.body .for.end; +.for.body: + power: int = call @pow ten index; + d: int = div in power; + check: bool = eq d zero; + br check .if.true .if.false; + .if.true: + not_finished: bool = const false; + jmp .for.cond; + .if.false: + index: int = add index one; + jmp .for.cond; +.for.end: + exp: int = sub index one; + is_palindrome: bool = call @palindrome in exp; + print is_palindrome; +} + +@pow(base: int, exp: int): int { +res: int = const 1; +zero: int = const 0; +one: int = const 1; +not_finished: bool = const true; +.for.cond.pow: + br not_finished .for.body.pow .for.end.pow; +.for.body.pow: + finished: bool = eq exp zero; + br finished .if.true.pow .if.false.pow; + .if.true.pow: + not_finished: bool = const false; + jmp .for.cond.pow; + .if.false.pow: + res: int = mul res base; + exp: int = sub exp one; + jmp .for.cond.pow; +.for.end.pow: + ret res; +} + +@palindrome(in: int, len: int): bool { + is_palindrome: bool = const false; + zero: int = const 0; + two: int = const 2; + ten: int = const 10; + check: bool = le len zero; + br check .if.true.palindrome .if.false.palindrome; + .if.true.palindrome: + is_palindrome: bool = const true; + jmp .if.end.palindrome; + .if.false.palindrome: + power: int = call @pow ten len; + left: int = div in power; + v1: int = div in ten; + v2: int = mul v1 ten; + right: int = sub in v2; + is_equal: bool = eq left right; + br is_equal .if.true.mirror .if.false.mirror; + .if.true.mirror: + temp: int = mul power left; + temp: int = sub in temp; + temp: int = sub temp right; + next_in: int = div temp ten; + next_len: int = sub len two; + is_palindrome: bool = call @palindrome next_in next_len; + jmp .if.end.palindrome; + .if.false.mirror: + is_palindrome: bool = const false; + jmp .if.end.palindrome; + .if.end.palindrome: + ret is_palindrome; +} \ No newline at end of file diff --git a/benchmarks/core/palindrome.out b/benchmarks/core/palindrome.out new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/benchmarks/core/palindrome.out @@ -0,0 +1 @@ +true diff --git a/benchmarks/core/palindrome.prof b/benchmarks/core/palindrome.prof new file mode 100644 index 000000000..29ad635b8 --- /dev/null +++ b/benchmarks/core/palindrome.prof @@ -0,0 +1 @@ +total_dyn_inst: 298 diff --git a/benchmarks/core/totient.bril b/benchmarks/core/totient.bril new file mode 100644 index 000000000..02ac090e2 --- /dev/null +++ b/benchmarks/core/totient.bril @@ -0,0 +1,66 @@ +# ARGS: 2023 +@main (n: int) { + print n; + tot: int = call @totient n; + print tot; +} + +@totient (n: int): int { + result: int = id n; + p: int = const 2; + one: int = const 1; + zero: int = const 0; + +.for.set.cond: + pp: int = mul p p; + cond: bool = le pp n; + br cond .for.set.body .for.set.end; + +.for.set.body: + + npmod: int = call @mod n p; + if_cond: bool = eq npmod zero; + br if_cond .if_lbl .else_lbl; +.if_lbl: + +.while.set.cond: + + npmod: int = call @mod n p; + while_cond: bool = eq npmod zero; + br while_cond .while.body .while.end; + +.while.body: + npdiv: int = div n p; + n: int = id npdiv; + jmp .while.set.cond; + +.while.end: + + resdiv: int = div result p; + result: int = sub result resdiv; + +.else_lbl: + + p: int = add p one; + jmp .for.set.cond; + +.for.set.end: + + final_if_cond: bool = gt n one; + br final_if_cond .final_if_label .final_else_label; + +.final_if_label: + resdiv: int = div result n; + result: int = sub result resdiv; + +.final_else_label: + + ret result; +} + +@mod (a: int, b: int): int { + ad: int = div a b; + mad: int = mul b ad; + ans: int = sub a mad; + ret ans; +} diff --git a/benchmarks/core/totient.out b/benchmarks/core/totient.out new file mode 100644 index 000000000..0dd06b31d --- /dev/null +++ b/benchmarks/core/totient.out @@ -0,0 +1,2 @@ +2023 +1632 diff --git a/benchmarks/core/totient.prof b/benchmarks/core/totient.prof new file mode 100644 index 000000000..d8c8e70f1 --- /dev/null +++ b/benchmarks/core/totient.prof @@ -0,0 +1 @@ +total_dyn_inst: 253 diff --git a/benchmarks/mem/primitive-root.bril b/benchmarks/mem/primitive-root.bril new file mode 100644 index 000000000..c8a514e0e --- /dev/null +++ b/benchmarks/mem/primitive-root.bril @@ -0,0 +1,211 @@ +@rem(a: int, b: int): int { + quotient : int = div a b; + guess : int = mul b quotient; + rem : int = sub a guess; + ret rem; +} + +# returns m | n +@divides(m: int, n: int): bool { + zero : int = const 0; + quotient : int = div n m; + guess : int = mul m quotient; + rem : int = sub n guess; + res : bool = eq rem zero; + ret res; +} + +# prepends n to ns, assuming that ns is an array of length l +@prepend(n : int, ns: ptr, l : int) : ptr { + one: int = const 1; + new : int = add l one; + out : ptr = alloc new; + + i : int = const 0; + store out n; + curr : ptr = id ns; + curr2 : ptr = ptradd out one; + .repeat: + stop : bool = lt i l; + br stop .next .exit; + .next: + tmp : int = load curr; + store curr2 tmp; + i : int = add i one; + curr : ptr = ptradd curr one; + curr2 : ptr = ptradd curr2 one; + jmp .repeat; + .exit: + free ns; + ret out; +} + +# returns the smallest prime factor of n. requires n > 1. +@prime_factor(n: int): int { + guess : int = const 2; + inc : int = const 1; + + .continue: + square: int = mul guess guess; + continue : bool = lt square n; + works : bool = call @divides guess n; + br works .yay .inc; + .yay: + ret guess; + .inc: + guess : int = add guess inc; + br continue .continue .giveup; + + .giveup: + ret n; +} + +# stores the number of prime factors in num_factors and returns an array of them +# ans is padded by 0 due to alloc difficulties +@prime_factors(n: int, num_factors: ptr): ptr { + count : int = const 1; + zero : int = const 0; + one : int = const 1; + ans : ptr = alloc count; + store ans zero; + + .continue: + exit : bool = eq n one; + br exit .exit .next; + .next: + prime : int = call @prime_factor n; + .repeat: + n : int = div n prime; + divides : bool = call @divides prime n; + br divides .repeat .divided; + .divided: + tmp : int = sub count one; + ans : ptr = call @prepend prime ans count; + count : int = add count one; + jmp .continue; + .exit: + store num_factors count; + ret ans; + +} + +@modexp(a : int, k : int, m : int) : int { + zero : int = const 0; + one : int = const 1; + two : int = const 2; + a : int = call @rem a m; + + + eq_zero : bool = eq zero k; + br eq_zero .exp_zero .not_zero; + + .exp_zero: + ret one; + + .not_zero: + eq_one : bool = eq one k; + br eq_one .exp_one .not_one; + + .exp_one: + ret a; + + .not_one: + rem_two : int = call @rem k two; + post_mul : bool = eq rem_two one; + + half_exp : int = div k two; + sqrt : int = call @modexp a half_exp m; + res : int = mul sqrt sqrt; + res : int = call @rem res m; + + br post_mul .post_multiply .no_post; + + .post_multiply: + res : int = mul res a; + res : int = call @rem res m; + .no_post: + + .exit: + ret res; +} + +@check_ord(p : int, phi_p : int, factors : ptr, guess : int) : bool { + count : int = const 0; + zero : int = const 0; + one : int = const 1; + ptr : ptr = id factors; + + .check_power: + factor : int = load ptr; + stop : bool = eq factor zero; + br stop .ret_true .next1; + + .next1: + power : int = div phi_p factor; + exp : int = call @modexp guess power p; + is_one : bool = eq exp one; + br is_one .ret_false .next2; + + .next2: + ptr : ptr = ptradd ptr one; + count : int = add count one; + jmp .check_power; + + .ret_true: + t : bool = const true; + ret t; + + .ret_false: + t : bool = const false; + ret t; +} + + +@search_primitive(p : int, phi_p : int, factors : ptr, start : int) : int { + fallback : int = const -999; + one : int = const 1; + + guess : int = id start; + + .eval: + too_big : bool = ge guess p; + br too_big .done_guess .keep_trying; + + .keep_trying: + works : bool = call @check_ord p phi_p factors guess; + br works .ret .inc; + + .ret: + ret guess; + + .inc: + guess : int = add guess one; + jmp .eval; + + + .done_guess: + ret fallback; +} + + +@phi(p : int) : int { + one : int = const 1; + q : int = sub p one; + ret q; +} + +# ARGS: 1151 +@main(p : int) { + zero : int = const 0; + one : int = const 1; + phi_p : int = call @phi p; + count_result : ptr = alloc one; + prime_factors : ptr = call @prime_factors phi_p count_result; + num_factors : int = load count_result; + + res : int = call @search_primitive p phi_p prime_factors one; + print res; + + free count_result; + free prime_factors; +} diff --git a/benchmarks/mem/primitive-root.out b/benchmarks/mem/primitive-root.out new file mode 100644 index 000000000..98d9bcb75 --- /dev/null +++ b/benchmarks/mem/primitive-root.out @@ -0,0 +1 @@ +17 diff --git a/benchmarks/mem/primitive-root.prof b/benchmarks/mem/primitive-root.prof new file mode 100644 index 000000000..198c2b3fc --- /dev/null +++ b/benchmarks/mem/primitive-root.prof @@ -0,0 +1 @@ +total_dyn_inst: 11029 diff --git a/benchmarks/mem/quicksort.bril b/benchmarks/mem/quicksort.bril new file mode 100644 index 000000000..b7e0f7f15 --- /dev/null +++ b/benchmarks/mem/quicksort.bril @@ -0,0 +1,125 @@ +# An implementation of Quicksort using the Lomuto partition scheme, adapted from the pseudocode on Wikipedia +# Input: an array of length 6 +# Output: the input array sorted in ascending order +# Adopted two helper functions, pack and print_array, from Jiajie Li's bubble sort benchmark + +# ARGS: 94 21 5 6 82 46 +@main(n1: int, n2: int, n3: int, n4: int, n5: int, n6:int) { + size: int = const 6; + array: ptr = call @pack size n1 n2 n3 n4 n5 n6; + one: int = const 1; + size_minus_one: int = sub size one; + + zero: int = const 0; + call @qsort array zero size_minus_one; + + # Print array + call @print_array array size; + + free array; +} + +@qsort(array : ptr, l: int, r:int) { + # if l >= r or r < 0, return + l_ge_r: bool = ge l r; + zero: int = const 0; + neg_r: bool = lt r zero; + ret_cond: bool = or l_ge_r neg_r; + + br ret_cond .done .continue; + + .continue: + p: int = call @partition array l r; + one: int = const 1; + p_minus_one: int = sub p one; + p_plus_one: int = add p one; + + call @qsort array l p_minus_one; + call @qsort array p_plus_one r; + + .done: + ret; +} + +@partition(array : ptr, l: int, r:int) : int { + # choose the last element as the pivot + pivot_loc: ptr = ptradd array r; + pivot: int = load pivot_loc; + one: int = const 1; + i:int = sub l one; + j:int = id i; + + .loop.init: + j:int = add j one; + cond: bool = lt j r; + br cond .body .post.loop; + + .body: + j_loc: ptr = ptradd array j; + a_j: int = load j_loc; + swap_cond: bool = le a_j pivot; + br swap_cond .swap .loop.init; + + .swap: + # increment i by 1 + i: int = add i one; + + # swap the current element with the element at the temporary pivot index i + i_loc: ptr = ptradd array i; + a_i: int = load i_loc; + store j_loc a_i; + store i_loc a_j; + jmp .loop.init; + + .post.loop: + i:int = add i one; + i_loc: ptr = ptradd array i; + a_i: int = load i_loc; + store i_loc pivot; + store pivot_loc a_i; + ret i; +} + +# allocate an array of the given size and pack the input values into the array +@pack(size: int, n1: int, n2: int, n3: int, n4: int, n5: int, n6: int) : ptr { + one: int = const 1; + i: int = const 0; + array: ptr = alloc size; +# Pack data into array manually. Cannot use loop because of the different var name. + loc: ptr = ptradd array i; + store loc n1; + i: int = add i one; + loc: ptr = ptradd array i; + store loc n2; + i: int = add i one; + loc: ptr = ptradd array i; + store loc n3; + i: int = add i one; + loc: ptr = ptradd array i; + store loc n4; + i: int = add i one; + loc: ptr = ptradd array i; + store loc n5; + i: int = add i one; + loc: ptr = ptradd array i; + store loc n6; + + ret array; +} + +@print_array(array: ptr, size: int) { + i: int = const 0; + one: int = const 1; +.loop: + cond: bool = lt i size; + br cond .body .done; +.body: + loc: ptr = ptradd array i; + val: int = load loc; + print val; +.loop_end: + i: int = add i one; + jmp .loop; +.done: + ret; +} \ No newline at end of file diff --git a/benchmarks/mem/quicksort.out b/benchmarks/mem/quicksort.out new file mode 100644 index 000000000..0ab8cac08 --- /dev/null +++ b/benchmarks/mem/quicksort.out @@ -0,0 +1,6 @@ +5 +6 +21 +46 +82 +94 diff --git a/benchmarks/mem/quicksort.prof b/benchmarks/mem/quicksort.prof new file mode 100644 index 000000000..32b1ccbf7 --- /dev/null +++ b/benchmarks/mem/quicksort.prof @@ -0,0 +1 @@ +total_dyn_inst: 264 diff --git a/docs/tools/bench.md b/docs/tools/bench.md index 4c3b6570c..5339ead57 100644 --- a/docs/tools/bench.md +++ b/docs/tools/bench.md @@ -21,6 +21,7 @@ The current benchmarks are: * `conjugate-gradient`: Uses conjugate gradients to solve `Ax=b` for any arbitrary positive semidefinite `A`. * `cordic`: Print an approximation of sine(radians) using 8 iterations of the [CORDIC algorithm](https://en.wikipedia.org/wiki/CORDIC). * `digial-root`: Computes the digital root of the input number. +* `dot-product`: Computes the dot product of two vectors. * `eight-queens`: Counts the number of solutions for *n* queens problem, a generalization of [Eight queens puzzle][eight_queens]. * `euclid`: Calculates the greatest common divisor between two large numbers using the [Euclidean Algorithm][euclid] with a helper function for the modulo operator. * `euler`: Approximates [Euler's number][euler] using the Taylor series. @@ -36,15 +37,19 @@ The current benchmarks are: * `mat-inv` : Calculates the inverse of a 3x3 matrix and prints it out. * `mat-mul`: Multiplies two `nxn` matrices using the [naive][matmul] matrix multiplication algorithm. The matrices are randomly generated using a [linear congruential generator][rng]. * `max-subarray`: solution to the classic Maximum Subarray problem. +* `mod_inv`: Calculates the [modular inverse][modinv] of `n` under to a prime modulus p. * `newton`: Calculate the square root of 99,999 using the [newton method][newton] * `n_root`: Calculate nth root of a float using newton's method. * `orders`: Compute the order ord(u) for each u in a cyclic group [][cgroup] of integers modulo *n* under the group operation + (modulo *n*). Set the second argument *is_lcm* to true if you would like to compute the orders using the lowest common multiple and otherwise the program will use the greatest common divisor. * `pascals-row`: Computes a row in Pascal's Triangle. +* `palindrome`: Outputs a 0-1 value indicating whether the input is a [palindrome][palindrome] number. * `perfect`: Check if input argument is a perfect number. Returns output as Unix style return code. * `pow`: Computes the n^th power of a given (float) number. * `primes-between`: Print the primes in the interval `[a, b]`. +* `primitive-root`: Computes a [primitive root][primitive_root] modulo a prime number input. * `pythagorean_triple`: Prints all Pythagorean triples with the given c, if such triples exist. An intentionally very naive implementation. * `quadratic`: The [quadratic formula][qf], including a hand-rolled implementation of square root. +* `quicksort`: [Quicksort using the Lomuto partition scheme][qsort]. * `recfact`: Compute *n!* using recursive function calls. * `rectangles-area-difference`: Output the difference between the areas of rectangles (as a positive value) given their respective side lengths. * `relative-primes`: Print all numbers relatively prime to *n* using [Euclidean algorithm][euclidean_into]. @@ -54,6 +59,7 @@ The current benchmarks are: * `sum-bit`: Print the number of 1-bits in the binary representation of the input integer. * `sum-divisors`: Prints the positive integer divisors of the input integer, followed by the sum of the divisors. * `sum-sq-diff`: Output the difference between the sum of the squares of the first *n* natural numbers and the square of their sum. +* `totient`: Computes [Euler's totient function][totient] on an input integer *n*. * `two-sum`: Print the indices of two distinct elements in the list [2, 7, 11, 13] whose sum equals the input. * `up-arrow`: Computes [Knuth's up arrow][uparrow] notation, with the first argument being the number, the second argument being the number of Knuth's up arrows, and the third argument being the number of repeats. * `vsmul`: Multiplies a constant scalar to each element of a large array. Tests the performance of vectorization optimizations. @@ -83,6 +89,11 @@ Credit for several of these benchmarks goes to Alexa VanHattum and Gregory Yaune [adler32]: https://en.wikipedia.org/wiki/Adler-32 [uparrow]: https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation [riemann]: https://en.wikipedia.org/wiki/Riemann_sum +[primitive_root]: https://en.wikipedia.org/wiki/Primitive_root_modulo_n [mandelbrot]: https://en.wikipedia.org/wiki/Mandelbrot_set +[palindrome]: https://en.wikipedia.org/wiki/Palindrome [hanoi]: https://en.wikipedia.org/wiki/Tower_of_Hanoi [euler]: https://en.wikipedia.org/wiki/E_(mathematical_constant) +[qsort]: https://en.wikipedia.org/wiki/Quicksort#Lomuto_partition_scheme +[modinv]: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse +[totient]: https://en.wikipedia.org/wiki/Euler's_totient_function