Week 2 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 2 spec.
How to use this
Run python tools/test.py --compiler PATH --week 2 -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/alloc_read_before_write | render | reading an alloc before any store sees zeros | C alloc not zeroed C rank-1 store one elem final version not followed |
render/alloc_scratch | render | a local alloc holds an intermediate, and it starts as zeros | final version not followed |
render/alloc_two_distinct | render | two same-size allocs are separate arrays | C alloc not zeroed C rank-1 store one elem allocs share storage final version not followed |
render/asserts_ignored | render | render ignores asserts and statements the outputs don't need | none recorded |
render/bool_ops | render | bool xor, where on bools, and bool to i32 | C where swapped |
render/call_inlined | render | calls render inline: the body runs on loaded args | C rank-1 store one elem |
render/call_nested_closed | render | a nested call's body keeps its own param 0 when inlined | none recorded |
render/casts_to_i32_and_bool | render | f32 to i32 truncates, and anything to bool is != 0, nan included | C cast to i32 rounds |
render/copy_scalar | render | the smallest kernel: one load, one store | none recorded |
render/dynamic_offset_bool | render | a bool-derived offset picks one of two elements | none recorded |
render/dynamic_offset_store | render | store at a masked loaded offset | none recorded |
render/f32_chain | render | a longer f32 chain where every intermediate rounds | none recorded |
render/f32_exact_literals | render | f32 constants need every digit: 1/3 and the largest finite f32 must come out exact | C float literals 6 digits |
render/f32_from_i32_cast | render | i32 to f32 rounds to nearest even, and bool to f32 is 0 or 1 | C cast to i32 rounds |
render/f32_max | render | max with nan on either side and both signed-zero orders | C max ignores -0.0 C max ignores nan |
render/f32_recip | render | recip of zeros, infinities and a value that rounds | none recorded |
render/f32_rounding | render | every f32 op rounds; -ffp-contract=off keeps a*b+c from fusing | none recorded |
render/f32_signed_zero | render | -0.0 survives where it should and doesn't where it shouldn't | none recorded |
render/f32_specials | render | nan and inf arithmetic | none recorded |
render/f32_where_cmplt | render | cmplt with nan is false, and where picks a branch | C cmplt as <= C where swapped |
render/i32_add_wrap | render | i32 add wraps, and in C that has to go through uint32_t | C plain signed add |
render/i32_div_mod_intmin | render | INT_MIN / -1 and INT_MIN % -1 are UB in C; the kernel must not trap | C div without -1 guard C mod without -1 guard |
render/i32_div_mod_trunc | render | C division truncates toward zero, and mod takes the dividend's sign | none recorded |
render/i32_max_cmp_xor | render | max, xor and a cmplt-driven where on i32 edges | C where swapped |
render/i32_mul_wrap | render | i32 mul wraps without UB | C plain signed add |
render/i32_shl_wrap | render | shl into the sign bit and past it | C plain signed add |
render/load_whole_index | render | load a whole buffer and index it | none recorded |
render/mixed_dtypes | render | three buffers of different dtypes in one kernel | loads not before stores |
render/nested_view_offsets | render | shrink of shrink: offsets add in the C too | C nested offsets lost C rank-1 load no offset |
render/old_version_late_use | render | an old-version load whose value is used two stores later | loads not before stores |
render/rank1_copy | render | load a [3] view and store it into another buffer's [3] view | C rank-1 load no offset |
render/rmw_chain | render | three increments of the same element | none recorded |
render/special_data_copy | render | special float data copies through bit for bit | none recorded |
render/stack_index_dynamic | render | index a stack with a loaded index | none recorded |
render/store_whole_buffer | render | store a rank-1 value into a whole buffer | none recorded |
render/swap_schedule | render | swap: the second store uses a load of the original version, so loads must be scheduled first | loads not before stores |
render/unwritten_buffer | render | a buffer the kernel never writes keeps its data | none recorded |
run/alloc_distinct | run: pass | two allocs of the same size are different memory | alloc not zeroed |
run/alloc_scratch | run: pass | alloc is local scratch that starts as zeros | alloc not zeroed |
run/bounds_bool_offset | run: pass | a bool cast to i32 is in [0, 1], so it can index a 2-element buffer | load ignores the offset |
run/bounds_const_arith_offset | run: pass | constant arithmetic offsets have exact bounds | load ignores the offset |
run/bounds_last_window | run: pass | a [k] view that ends exactly at the end is in bounds | load ignores the offset |
run/bounds_masked_offset | run: pass | a loaded index masked with max and mod is provably in [0, 3] | none recorded |
run/buf_bool | run: pass | bool buffers hold true/false | none recorded |
run/buf_chain3 | run: pass | three stores in a row, each into the previous version | none recorded |
run/buf_data_load | run: pass | data= gives a buffer its initial contents, and a scalar view loads one element | none recorded |
run/buf_f32_rounding | run: pass | f32 data rounds like f32 literals, and stays f32 through memory | none recorded |
run/buf_old_version | run: pass | a load of the old version still sees the old value, even if written after the store in the file | none recorded |
run/buf_rmw | run: pass | read, modify, write back | none recorded |
run/buf_store_after | run: pass | after names the version with the store applied | none recorded |
run/buf_versions_differ | run: pass | every version keeps its own contents | none recorded |
run/buf_zeros | run: pass | without data=, a buffer starts as zeros of its dtype | none recorded |
run/copy_between_buffers | run: pass | load from one buffer, store into another | rank-1 store writes one |
run/err_after_not_store | run: invalid | the second operand of after must be a store | none recorded |
run/err_after_on_view | run: invalid | after takes a whole buffer version, not a view | none recorded |
run/err_after_stale_version | run: invalid | after names an older version of the right buffer: still wrong | after version unchecked |
run/err_after_wrong_version | run: invalid | after must name the version the store writes into | after version unchecked |
run/err_alloc_slot | run: invalid | alloc has no slot: it isn't an input | none recorded |
run/err_bounds_mask_too_wide | run: invalid | masking to [0, 4] doesn't fit a scalar view in a 4-element buffer | bounds off by one bounds unchecked |
run/err_bounds_mod_negative | run: invalid | mod alone keeps the sign, so the offset could be negative | bounds allow negative bounds unchecked |
run/err_bounds_negative | run: invalid | negative offsets are out of bounds | bounds allow negative bounds unchecked |
run/err_bounds_nested | run: invalid | a nested view must fit its parent view, not just the buffer | bounds off by one nested view vs storage |
run/err_bounds_past_end | run: invalid | elements 3 and 4 of a 4-element buffer: 4 is past the end | bounds off by one |
run/err_bounds_raw_loaded | run: invalid | a raw loaded index could be anything, so the view can't be proven in bounds | none recorded |
run/err_bounds_scalar_at_n | run: invalid | element 4 of a 4-element buffer doesn't exist | none recorded |
run/err_bounds_unused | run: invalid | bounds are checked statically, even for a view nothing uses | none recorded |
run/err_buffer_no_slot | run: invalid | buffer needs slot= | none recorded |
run/err_buffer_size_zero | run: invalid | a buffer has at least one element | none recorded |
run/err_data_dtype | run: invalid | data values must be literals of the buffer's dtype | data dtype unchecked |
run/err_data_length | run: invalid | data= must give exactly n values | data length unchecked |
run/err_duplicate_slot | run: invalid | slots are unique | duplicate slots ok |
run/err_duplicate_slot_identical | run: invalid | two identical buffer statements are still two buffers sharing a slot | duplicate slots ok identical buffers merge |
run/err_load_value | run: invalid | load reads memory, not a value | none recorded |
run/err_mem_alu_operand | run: invalid | memory isn't a value: load it first | none recorded |
run/err_mem_assert | run: invalid | assert compares values, not memory | memory as ALU operand ok |
run/err_mem_call_arg | run: invalid | memory can't be passed to a call | none recorded |
run/err_mem_in_call_body | run: invalid | memory can't be used inside a call body | memory in call body ok |
run/err_mem_stack | run: invalid | memory can't be stacked | memory as ALU operand ok |
run/err_offset_f32 | run: invalid | offsets are i32 | none recorded |
run/err_old_version_needed_late | run: invalid | the offset of a load from the old version depends on the new version: no order works | no ordering cycle check |
run/err_shrink_no_shape | run: invalid | shrink needs shape= | none recorded |
run/err_store_dtype | run: invalid | the stored value must have the view's dtype | store type unchecked |
run/err_store_into_value | run: invalid | store writes into memory, not into a value | none recorded |
run/err_store_shape | run: invalid | the stored value must have the view's shape | store type unchecked |
run/err_two_stores_one_version | run: invalid | two stores into the same version are unordered | two stores per version |
run/err_two_stores_unsequenced_second | run: invalid | a second store into a version is invalid even if only one is sequenced | none recorded |
run/err_unsequenced_store | run: invalid | a store no after sequences has no effect, so it's an error | unsequenced store ok |
run/err_view_alu_operand | run: invalid | a view isn't a value either | memory as ALU operand ok |
run/fail_memory_assert | run: assert_fail | a failing assert on a loaded value | none recorded |
run/load_after_values_in_call | run: pass | loaded values (not memory) can be call args | none recorded |
run/loaded_div_zero | run: invalid | a runtime error in a value an assert depends on: dividing by a loaded zero | none recorded |
run/special_float_data | run: pass | data can hold nan, inf, -inf and -0.0 | none recorded |
run/swap | run: pass | swap two elements: both loads read the original version | none recorded |
run/unread_store_ok | run: pass | a sequenced store that no assert reads is fine | none recorded |
run/view_rank1_load | run: pass | shape=[k] views k elements starting at the offset | none recorded |
run/view_rank1_store | run: pass | store a rank-1 value into a [k] view | rank-1 store writes one |
run/view_shrink_of_shrink | run: pass | offsets add: shrink 1 then shrink 2 lands on element 3 | shrink offsets don't add |
run/view_shrink_of_shrink_store | run: pass | a store through a nested view writes the right element | shrink offsets don't add |
run/view_whole_load_index | run: pass | load a whole buffer, then index the rank-1 value | none recorded |