Skip to content

Commit

Permalink
Add regression tests for loop unrolling (#49)
Browse files Browse the repository at this point in the history
record current status before making changes to unrolling
  • Loading branch information
tim-hoffman authored Aug 26, 2023
1 parent 58c3c94 commit 2a69d6a
Show file tree
Hide file tree
Showing 15 changed files with 316 additions and 26 deletions.
16 changes: 13 additions & 3 deletions circom/tests/loops/fib_input.circom
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

template Fibonacci() {
signal input nth_fib;
Expand All @@ -11,7 +11,7 @@ template Fibonacci() {
var next = 0;

var counter = nth_fib;
while (counter > 2) {
while (counter > 2) { // unknown iteration count
next = a + b;
a = b;
b = next;
Expand All @@ -22,4 +22,14 @@ template Fibonacci() {
out <-- (nth_fib == 0) ? 0 : (nth_fib == 1 ? 1 : a + b);
}

component main = Fibonacci();
component main = Fibonacci();

//// Use the block labels to check that the loop is NOT unrolled
//CHECK-LABEL: define void @Fibonacci_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: loop.cond{{.*}}:
//CHECK: loop.body{{.*}}:
//CHECK: loop.end{{.*}}:
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: }
22 changes: 19 additions & 3 deletions circom/tests/loops/fib_template.circom
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

template FibonacciTmpl(N) {
signal output out;
Expand All @@ -10,7 +10,7 @@ template FibonacciTmpl(N) {
var next = 0;

var counter = N;
while (counter > 2) {
while (counter > 2) { // known iteration count
next = a + b;
a = b;
b = next;
Expand All @@ -27,4 +27,20 @@ template FibonacciTmpl(N) {
}
}

component main = FibonacciTmpl(5);
component main = FibonacciTmpl(5);

//CHECK-LABEL: define void @FibonacciTmpl_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//// Use the block labels to check that the loop is unrolled
//CHECK-NOT: loop.cond{{.*}}:
//CHECK-NOT: loop.body{{.*}}:
//CHECK-NOT: loop.end{{.*}}:
//CHECK: unrolled_loop{{.*}}:
//CHECK-NOT: loop.cond{{.*}}:
//CHECK-NOT: loop.body{{.*}}:
//CHECK-NOT: loop.end{{.*}}:
//// Check that final value stored to 'out' is computed correctly via unrolling
//CHECK: store{{[0-9]+}}:
//CHECK: %[[T:[0-9]+]] = getelementptr [0 x i256], [0 x i256]* %{{.*}}[[ARG]], i32 0, i32 0
//CHECK: store i256 5, i256* %{{.*}}[[T]], align 4
//CHECK: }
20 changes: 18 additions & 2 deletions circom/tests/loops/for_known.circom
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

template ForKnown(N) {
signal output out;
Expand All @@ -13,4 +13,20 @@ template ForKnown(N) {
out <-- acc;
}

component main = ForKnown(10);
component main = ForKnown(10);

//CHECK-LABEL: define void @ForKnown_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//// Use the block labels to check that the loop is unrolled
//CHECK-NOT: loop.cond{{.*}}:
//CHECK-NOT: loop.body{{.*}}:
//CHECK-NOT: loop.end{{.*}}:
//CHECK: unrolled_loop{{.*}}:
//CHECK-NOT: loop.cond{{.*}}:
//CHECK-NOT: loop.body{{.*}}:
//CHECK-NOT: loop.end{{.*}}:
//// Check that final value stored to 'out' is computed correctly via unrolling
//CHECK: store{{[0-9]+}}:
//CHECK: %[[T:[0-9]+]] = getelementptr [0 x i256], [0 x i256]* %{{.*}}[[ARG]], i32 0, i32 0
//CHECK: store i256 55, i256* %{{.*}}[[T]], align 4
//CHECK: }
14 changes: 12 additions & 2 deletions circom/tests/loops/for_unknown.circom
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

template ForUnknown() {
signal input in;
Expand All @@ -14,4 +14,14 @@ template ForUnknown() {
out <-- acc;
}

component main = ForUnknown();
component main = ForUnknown();

//// Use the block labels to check that the loop is NOT unrolled
//CHECK-LABEL: define void @ForUnknown_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: loop.cond{{.*}}:
//CHECK: loop.body{{.*}}:
//CHECK: loop.end{{.*}}:
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: }
14 changes: 12 additions & 2 deletions circom/tests/loops/for_unknown_index.circom
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

template ForUnknownIndex() {
signal input in;
Expand All @@ -17,4 +17,14 @@ template ForUnknownIndex() {
out <-- arr[acc];
}

component main = ForUnknownIndex();
component main = ForUnknownIndex();

//// Use the block labels to check that the loop is NOT unrolled
//CHECK-LABEL: define void @ForUnknownIndex_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: loop.cond{{.*}}:
//CHECK: loop.body{{.*}}:
//CHECK: loop.end{{.*}}:
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: }
71 changes: 68 additions & 3 deletions circom/tests/loops/inner_loops.circom
Original file line number Diff line number Diff line change
@@ -1,16 +1,81 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

template InnerLoops(n) {
signal input a[n];
var b[n];

for (var i = 0; i < n; i++) {
for (var j = 0; j <= i; j++) {
b[i] = a[i - j];
b[i] += a[i - j];
}
}
}

component main = InnerLoops(2);
component main = InnerLoops(2);
//
//ARG = { a[0], a[1] }
//lvars = { n, b[0], b[1], i, j }
//unrolled code:
// b[0] = b[0] + a[0 - 0 = 0];
// b[1] = b[1] + a[1 - 0 = 1];
// b[1] = b[1] + a[1 - 1 = 0];
//
//
//CHECK-LABEL: define void @InnerLoops_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//// Use the block labels to check that the loop is unrolled and check the unrolled body
//CHECK-NOT: loop.cond{{.*}}:
//CHECK-NOT: loop.body{{.*}}:
//CHECK-NOT: loop.end{{.*}}:
//CHECK: unrolled_loop{{.*}}:
// // j = 0
//CHECK-NEXT: %[[T01:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 4
//CHECK-NEXT: store i256 0, i256* %{{.*}}[[T01]], align 4
// // b[0] = b[0] + a[0]
//CHECK-NEXT: %[[T02:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 1
//CHECK-NEXT: %[[T03:[[:alnum:]_.]+]] = load i256, i256* %{{.*}}[[T02]], align 4
//CHECK-NEXT: %[[T04:[[:alnum:]_.]+]] = getelementptr [0 x i256], [0 x i256]* %{{.*}}[[ARG]], i32 0, i32 0
//CHECK-NEXT: %[[T05:[[:alnum:]_.]+]] = load i256, i256* %{{.*}}[[T04]], align 4
//CHECK-NEXT: %[[T06:[[:alnum:]_.]+]] = call i256 @fr_add(i256 %{{.*}}[[T03]], i256 %{{.*}}[[T05]])
//CHECK-NEXT: %[[T07:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 1
//CHECK-NEXT: store i256 %{{.*}}[[T06]], i256* %{{.*}}[[T07]], align 4
// // j = 1
//CHECK-NEXT: %[[T08:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 4
//CHECK-NEXT: store i256 1, i256* %{{.*}}[[T08]], align 4
// // i = 1
//CHECK-NEXT: %[[T09:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 3
//CHECK-NEXT: store i256 1, i256* %{{.*}}[[T09]], align 4
// // j = 0
//CHECK-NEXT: %[[T10:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 4
//CHECK-NEXT: store i256 0, i256* %{{.*}}[[T10]], align 4
// // b[1] = b[1] + a[1]
//CHECK-NEXT: %[[T11:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 2
//CHECK-NEXT: %[[T12:[[:alnum:]_.]+]] = load i256, i256* %{{.*}}[[T11]], align 4
//CHECK-NEXT: %[[T13:[[:alnum:]_.]+]] = getelementptr [0 x i256], [0 x i256]* %{{.*}}[[ARG]], i32 0, i32 1
//CHECK-NEXT: %[[T14:[[:alnum:]_.]+]] = load i256, i256* %{{.*}}[[T13]], align 4
//CHECK-NEXT: %[[T15:[[:alnum:]_.]+]] = call i256 @fr_add(i256 %{{.*}}[[T12]], i256 %{{.*}}[[T14]])
//CHECK-NEXT: %[[T16:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 2
//CHECK-NEXT: store i256 %{{.*}}[[T15]], i256* %{{.*}}[[T16]], align 4
// // j = 1
//CHECK-NEXT: %[[T17:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 4
//CHECK-NEXT: store i256 1, i256* %{{.*}}[[T17]], align 4
// // b[1] = b[1] + a[0]
//CHECK-NEXT: %[[T18:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 2
//CHECK-NEXT: %[[T19:[[:alnum:]_.]+]] = load i256, i256* %{{.*}}[[T18]], align 4
//CHECK-NEXT: %[[T20:[[:alnum:]_.]+]] = getelementptr [0 x i256], [0 x i256]* %{{.*}}[[ARG]], i32 0, i32 0
//CHECK-NEXT: %[[T21:[[:alnum:]_.]+]] = load i256, i256* %{{.*}}[[T20]], align 4
//CHECK-NEXT: %[[T22:[[:alnum:]_.]+]] = call i256 @fr_add(i256 %{{.*}}[[T19]], i256 %{{.*}}[[T21]])
//CHECK-NEXT: %[[T23:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 2
//CHECK-NEXT: store i256 %{{.*}}[[T22]], i256* %{{.*}}[[T23]], align 4
// // j = 2
//CHECK-NEXT: %[[T24:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 4
//CHECK-NEXT: store i256 2, i256* %{{.*}}[[T24]], align 4
// // i = 2
//CHECK-NEXT: %[[T25:[[:alnum:]_.]+]] = getelementptr [5 x i256], [5 x i256]* %lvars, i32 0, i32 3
//CHECK-NEXT: store i256 2, i256* %{{.*}}[[T25]], align 4
//CHECK-NOT: loop.cond{{.*}}:
//CHECK-NOT: loop.body{{.*}}:
//CHECK-NOT: loop.end{{.*}}:
//CHECK: }
2 changes: 1 addition & 1 deletion circom/tests/loops/inner_loops2.circom
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ template InnerLoops(n) {
i++; // 5
}

component main = InnerLoops(5);
component main = InnerLoops(5);
2 changes: 1 addition & 1 deletion circom/tests/loops/inner_loops3.circom
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ template InnerLoops(n) {
}
}

component main = InnerLoops(5);
component main = InnerLoops(5);
2 changes: 1 addition & 1 deletion circom/tests/loops/inner_loops4.circom
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ template InnerLoops(n) {
}
}

component main = InnerLoops(2);
component main = InnerLoops(2);
48 changes: 48 additions & 0 deletions circom/tests/loops/known_function.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

function funWithLoop(n) {
var acc = 0;
for (var i = 1; i <= n; i++) {
acc += i;
}
return acc;
}

template KnownFunctionArgs() {
signal output out[3];

out[0] <-- funWithLoop(4); // 0 + 1 + 2 + 3 + 4 = 10
out[1] <-- funWithLoop(5); // 0 + 1 + 2 + 3 + 4 + 5 = 15

var acc = 1;
for (var i = 2; i <= funWithLoop(3); i++) {
acc *= i;
}
out[2] <-- acc; // 1 * 2 * 3 * 4 * 5 * 6 = 720
}

component main = KnownFunctionArgs();

//CHECK-LABEL: define void @KnownFunctionArgs_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//// Check storing initial constant values to 'out'
//CHECK: store{{[0-9]+}}:
//CHECK: %[[T1:[0-9]+]] = getelementptr [0 x i256], [0 x i256]* %{{.*}}[[ARG]], i32 0, i32 0
//CHECK: store i256 10, i256* %{{.*}}[[T1]], align 4
//CHECK: store{{[0-9]+}}:
//CHECK: %[[T2:[0-9]+]] = getelementptr [0 x i256], [0 x i256]* %{{.*}}[[ARG]], i32 0, i32 1
//CHECK: store i256 15, i256* %{{.*}}[[T2]], align 4
//// Use the block labels to check that the loop is unrolled
//CHECK-NOT: loop.cond{{.*}}:
//CHECK-NOT: loop.body{{.*}}:
//CHECK-NOT: loop.end{{.*}}:
//CHECK: unrolled_loop{{.*}}:
//CHECK-NOT: loop.cond{{.*}}:
//CHECK-NOT: loop.body{{.*}}:
//CHECK-NOT: loop.end{{.*}}:
//// Check that final value stored to 'out' is computed correctly via unrolling
//CHECK: store{{[0-9]+}}:
//CHECK: %[[T3:[0-9]+]] = getelementptr [0 x i256], [0 x i256]* %{{.*}}[[ARG]], i32 0, i32 2
//CHECK: store i256 720, i256* %{{.*}}[[T3]], align 4
39 changes: 39 additions & 0 deletions circom/tests/loops/known_signal_value.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

template accumulate() {
signal input i;
signal output o;
var r = 0;
while (r < i) {
r++;
}
o <-- r;
}

template KnownLoopViaSignal() {
signal output y;

component a = accumulate();
a.i <-- 5;
y <-- a.o;
}

component main = KnownLoopViaSignal();

//// Use the block labels to check that the loop is NOT unrolled
//CHECK-LABEL: define void @accumulate_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: loop.cond{{.*}}:
//CHECK: loop.body{{.*}}:
//CHECK: loop.end{{.*}}:
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: }

//// Use the block labels to check that no loop related blocks are present
//CHECK-LABEL: define void @KnownLoopViaSignal_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//CHECK-NOT: {{.*}}loop{{.*}}:
//CHECK: }
14 changes: 12 additions & 2 deletions circom/tests/loops/unknown_local_array_index.circom
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma circom 2.0.0;
// REQUIRES: circom
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s
// RUN: rm -rf %t && mkdir %t && %circom --llvm -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s

template ForUnknownIndex() {
signal input in;
Expand All @@ -20,4 +20,14 @@ template ForUnknownIndex() {
out <-- arr2[acc % 10];
}

component main = ForUnknownIndex();
component main = ForUnknownIndex();

//// Use the block labels to check that the loop is NOT unrolled
//CHECK-LABEL: define void @ForUnknownIndex_{{[0-9]+}}_run
//CHECK-SAME: ([0 x i256]* %[[ARG:[0-9]+]])
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: loop.cond{{.*}}:
//CHECK: loop.body{{.*}}:
//CHECK: loop.end{{.*}}:
//CHECK-NOT: unrolled_loop{{.*}}:
//CHECK: }
Loading

0 comments on commit 2a69d6a

Please sign in to comment.