Week 3 test guide
A test failed and you don't know why? Find it here: what it checks, and the plausible bugs that are known to fail it. Then read the matching part of the week 3 spec.
How to use this
Run python tools/test.py --compiler PATH --week 3 -v, copy the failing test's name, and search below. "Bugs known to fail it" lists mistakes that were planted in the reference compiler and caught by this test, so one of them is a good first guess. It's not a complete list, and the debugging guide covers what to do when none fit.
| test | kind | what it checks | bugs known to fail it |
|---|---|---|---|
render/r_bounds_from_ranges | render | render version of run/bounds_from_ranges | end doesn't close its range render loop starts at 1 stores under ends unsequenced values hoisted to outer loops |
render/r_computed_bound | render | render version of run/computed_bound | end doesn't close its range stores under ends unsequenced |
render/r_conv1d_i32_pad2 | render | padding 2 on i32 | end doesn't close its range loads never move into loops render loop starts at 1 stores under ends unsequenced values hoisted to outer loops |
render/r_conv1d_padded | render | a padded conv1d with masked taps | loads never move into loops render loop starts at 1 render loop uses <= values hoisted to outer loops |
render/r_conv1d_valid | render | a valid conv1d | loads never move into loops |
render/r_conv2d_i32 | render | an i32 conv2d | render loop uses <= |
render/r_conv2d_valid | render | a valid conv2d | render loop uses <= |
render/r_dynamic_trip | render | a trip count loaded from memory: the index is masked with mod so the view is provable | none recorded |
render/r_gemm_1x5x1 | render | a dot product | none recorded |
render/r_gemm_2x3x2 | render | looped matmul in C | none recorded |
render/r_gemm_3x1x4 | render | an outer product (K = 1) | none recorded |
render/r_gemm_4x3x5 | render | a non-square matmul | none recorded |
render/r_gemm_f32_rounding | render | f32 accumulation order is exact: products of thirds | none recorded |
render/r_gemm_i32_wrap | render | i32 matmul wraps through uint32_t inside loops | none recorded |
render/r_in_loop_after_chain | render | render version of run/in_loop_after_chain | none recorded |
render/r_loop_carried_load | render | render version of run/loop_carried_load | none recorded |
render/r_loop_invariant_value | render | render version of run/loop_invariant_value | none recorded |
render/r_mixed_dtypes_loop | render | one loop writes f32, i32 and bool buffers | none recorded |
render/r_multi_effect_end | render | render version of run/multi_effect_end | none recorded |
render/r_negative_trip | render | render version of run/negative_trip | none recorded |
render/r_negzero_data | render | data=[-0.0 0.0] round-trips through a loop: recip of each lands in the output as -inf, inf | none recorded |
render/r_old_version_after_loop | render | render version of run/old_version_after_loop | none recorded |
render/r_prefix_f32 | render | an f32 running total | none recorded |
render/r_prefix_sum | render | a running total | none recorded |
render/r_reduce_max_nan | render | a max accumulator meets nan | none recorded |
render/r_reduce_max_negzero | render | max over -0.0 values | none recorded |
render/r_reduce_product_wrap | render | an i32 product accumulator wraps | none recorded |
render/r_reduce_sum_order | render | 1e8 + 1 + -1e8 + 1 sums to 1.0 in f32 | none recorded |
render/r_rowsum | render | row sums | none recorded |
render/r_skip_middle_range | render | render version of run/skip_middle_range | nesting demands every range |
render/r_split_use_across_asserts | render | render must place the load where the whole program says, even though render ignores the assert that pins it | loads follow their deepest use placement sees only the roots |
render/r_split_use_load | render | render version of run/split_use_load | none recorded |
render/r_three_deep | render | three nested loops writing a 2x3x2 index cube | none recorded |
render/r_transpose_3x4 | render | transpose | none recorded |
render/r_transpose_f32 | render | f32 transpose keeps -0.0 and nan | none recorded |
render/r_wrap_in_loop | render | adding 2^30 four times wraps through INT_MIN back to 0 | none recorded |
render/r_zero_trip | render | render version of run/zero_trip | none recorded |
run/bounds_from_ranges | run: pass | i*4 + k with i < 3 and k < 4 is provably inside 12 elements | loop runs one extra time |
run/computed_bound | run: pass | a range bound can be any i32 with no open ranges | loop runs one extra time |
run/conv1d_i32_padded2 | run: pass | padding of 2 on i32 | loop runs one extra time |
run/conv1d_padded | run: pass | a padded convolution masks out-of-range taps with where, after clamping the index into a provable range | none recorded |
run/conv1d_valid | run: pass | a valid 1-d convolution: output o sums x[o+k]*w[k] | none recorded |
run/conv2d_valid | run: pass | a valid 2-d convolution: four nested loops | none recorded |
run/err_after_loop_stale_version | run: invalid | after names %o, but the loop stores into %o0, the version after the reset: the versions would fork | none recorded |
run/err_after_wrong_memory_loop | run: invalid | after names memory the loop never stores into | after-on-loop skips memory check |
run/err_assert_closed_range | run: invalid | an assert can't read a range even when the range's loop is closed elsewhere | asserts may read open ranges |
run/err_assert_open_range | run: invalid | an assert can't depend on an open range | none recorded |
run/err_call_open_range | run: invalid | a call can't take an argument that depends on an open range | calls may take open-range args |
run/err_end_last_not_range | run: invalid | the last operand of end must be a range | none recorded |
run/err_end_unused_range | run: invalid | end closes a range its effect doesn't use | none recorded |
run/err_end_unused_range_closed | run: invalid | end over j repeats a store that doesn't use j. Otherwise j is well formed, so only the 'end must use its range' rule rejects it | end need not use its range |
run/err_end_value_not_effect | run: invalid | end repeats effects, not values | end accepts values |
run/err_loop_never_closed | run: invalid | a store inside a loop that no end closes | unclosed loops allowed |
run/err_loop_store_unsequenced | run: invalid | the end runs the store, but no after sequences the loop | none recorded |
run/err_memory_in_call_body_loop | run: invalid | a call body can't read memory, even when called from a loop | none recorded |
run/err_range_bound_depends | run: invalid | a range's bound can't depend on another range (no triangular loops in week 3) | bounds may depend on ranges |
run/err_range_f32_bound | run: invalid | a range bound is an i32 | none recorded |
run/err_range_overrun | run: invalid | range 4 indexes a 3-element buffer: offset in [0, 3] can't be proven inside | range interval is [0, 0] range interval one too tight |
run/err_range_two_ends | run: invalid | a range is closed by exactly one end | a range may close twice |
run/err_range_value_escapes | run: invalid | a value that depends on a range can't leak into an assert outside the loop | asserts may read open ranges |
run/err_two_stores_one_version_loop | run: invalid | two stores into the same version inside one loop body are unordered | none recorded |
run/err_wrong_stride | run: invalid | i*3 + k with k < 4 overruns a 6-element buffer at i=1, k=3 | range interval is [0, 0] range interval one too tight |
run/gemm_1xK | run: pass | a 1xK @ Kx1 matmul is a dot product | none recorded |
run/gemm_2x3x2 | run: pass | matmul by hand: three loops, an accumulator reset once per output element | none recorded |
run/gemm_Kx1_outer | run: pass | Kx1 @ 1xN with K=1 inner loop is an outer product | none recorded |
run/gemm_i32_wrap | run: pass | i32 matmul wraps inside the loop | none recorded |
run/gemm_nonsquare | run: pass | 3x4 @ 4x2 | none recorded |
run/in_loop_after_chain | run: pass | two stores into one element in one iteration: the second reads the first through an after inside the loop | none recorded |
run/loop_assert_fail | run: assert_fail | the loop writes squares, but the assert expects the wrong last value | none recorded |
run/loop_carried_load | run: pass | a load whose only use is inside a loop that stores into its version is reloaded every iteration: 10, 11, 12, 13 | none recorded |
run/loop_invariant_value | run: pass | a value with no range dependence is the same in every iteration | none recorded |
run/loop_runtime_error | run: invalid | a division by zero inside a loop an assert depends on is a runtime error | none recorded |
run/multi_effect_end | run: pass | one end can repeat several stores into different buffers | none recorded |
run/negative_trip | run: pass | a negative bound also runs zero times | zero-trip loops run once |
run/negzero_a_plus | run: pass | a buffer of +0.0 (parsed before negzero_b_minus, with the same names) | none recorded |
run/negzero_b_minus | run: pass | data=[-0.0] must survive even if an identical buffer of +0.0 was built earlier: recip tells them apart | none recorded |
run/old_version_after_loop | run: pass | a load of the pre-loop version, used after the loop, still sees the old contents | none recorded |
run/pinned_load_outside | run: pass | the load's first use (x + 1) doesn't depend on the range, so both run once before the loop: every iteration writes 11 | none recorded |
run/prefix_sum | run: pass | a running total: two stores per iteration into two different memories, one end | loop runs backwards |
run/prefix_sum_wrap | run: pass | a running total that wraps | loop runs backwards |
run/reduce_max_nan | run: pass | a max accumulator that meets nan stays nan | none recorded |
run/reduce_max_plain | run: pass | a max accumulator | none recorded |
run/reduce_product_wrap | run: pass | a product accumulator wraps in i32 | none recorded |
run/reduce_sum_f32_order | run: pass | the sum runs in increasing k: 1e8 + 1 + -1e8 + 1 is 1.0 in f32, not 2.0 | loop runs backwards |
run/rowsum_3x4 | run: pass | row sums: one accumulator per row, reset by a store that depends on the row | none recorded |
run/skip_middle_range | run: pass | an inner loop inside two loops can ignore the middle range, and it just reruns | nesting demands every range |
run/split_use_across_asserts | run: pass | where a load runs depends on every use in the program: the second assert uses %x outside the loop, so the loop sees the pre-loop 10 every time, even while checking the first assert alone | loads follow their deepest use placement sees only the roots |
run/split_use_load | run: pass | a load with uses inside and outside the loop runs once, before it: every iteration writes 10 + 1 | none recorded |
run/transpose_2x3 | run: pass | transpose: read row-major, write column-major | none recorded |
run/zero_trip | run: pass | range 0 runs zero times: the buffer keeps its data | zero-trip loops run once |