Overview
Assertions in instruction sequences are checks embedded directly in a generated instruction trace. The TestRIG paper describes sequences that can include assertions, such as checking that the value written by the previous instruction was non-zero. These assertions allow a generated sequence to fail even when no divergence has been observed between tandem executions or reference comparisons. The authors note that, unusually, such sequences do not require tandem verification to discover a failure, and that they have used assertions to test the limits of implementation-defined behavior.
Role in randomized CPU testing
In the TestRIG/QCVEngine context, assertions complement randomized instruction generation and shrinking. A normal randomized test may expose a bug through divergence, after which shrinking reduces the counterexample. Assertions add another failure mechanism: the test can encode a property that must hold at a particular point in the instruction sequence. If the property is violated, the sequence fails even without a comparator-observed architectural mismatch.
Example: cache-counter assertion in a CHERI-RISC-V counterexample
One illustrated counterexample combines .noshrink and .assert. The sequence initializes counters, performs an illegal cSetBoundsImmediate that attempts to enlarge a CHERI capability bound, executes a load through the resulting illegal capability, delays for counter propagation, reads the L1 cache-miss counter, and then asserts:
.assert rd_wdata == 0x0 ""
The paper explains that .noshrink is used to initialize counter state so that the final assertion on the L1 cache-miss counter is deterministic. The same example is used to demonstrate a vulnerability: although CHERI only allows bounds to be reduced and the illegal cSetBoundsImmediate throws an exception, the capability that would have been produced is forwarded during a pipeline flush and causes a cache fill that could lead to side-channel attacks.
Relationship to non-shrinkable sequence regions
Assertions may be used alongside non-shrinkable regions. The cited example uses .noshrink to preserve initialization needed for determinism, while .assert records the expected final condition. This pairing is useful when shrinking would otherwise remove setup code required to make an assertion meaningful or repeatable.
Practical significance
Assertions make instruction sequences more expressive than pure divergence-based tests. They allow generated tests to encode properties over observed instruction results or machine counters, supporting checks for implementation-defined behavior and microarchitectural effects that may not appear as ordinary architectural divergences.