Steps to Reproduce
Save the following as test.cir and run:
V1 a 0 PULSE(0 5 1n 1n 1n 1u 2u)
V2 b 0 PULSE(0 5 1n 1n 1n 0.5u 2u)
Rz z 0 1meg
.func F(x,y) {if(x>1|y>1,9,0)}
E1 z 0 VALUE {F(V(a),V(b))}
.tran 0 5u
.end
Where | Does NOT Crash
- Inline in B / E / G VALUE {…} expressions
- Inside a .func with no parameters
- As an argument passed into a .func (not in the .func body)
- In subcircuit-level expressions
- QSPICE version and build:
- QUX.exe Build: Oct. 2 2026
- QSPICE64.exe Build: Sep. 11 2026
- QSPICE80.exe Build: Sep. 11 2026
- QPOST.exe Build: Sep. 9 2026
- Operating system
Windows 11, version 25H2 for x64-based Systems (KB5124008) (26200.9445)
1 Like
Hello VideanuCatalin
I didn’t make a netlist and I simply copy-pasted the contents of the netlist inside a text box in QSPICE and, it crashed. I removed the .end, same. With | or ||, it crashes in E/G/B sources. ^, ^^, &, && work. However, I’m on CachyOS and Wine staging threw this on me:
Unhandled exception: page fault on read access to 0x00000000 in wow64 32-bit code (0x00494507).
Register dump:
CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:002b
EIP:00494507 ESP:0026ac60 EBP:0026aca0 EFLAGS:00010246( R- -- I Z- -P- )
EAX:00000002 EBX:00000000 ECX:0026aca0 EDX:002ca068
ESI:002cb450 EDI:00000000
Stack dump:
0x0026ac60: 00000000 00000000 002cb540 00000010
0x0026ac70: 00266c30 00000000 0026ac50 0026ace0
0x0026ac80: 002ca088 0026aca0 002cb540 004943ca
0x0026ac90: 0026ace0 004943a8 0026accc 002cb360
0x0026aca0: 002cb2d0 002cb330 0049432a 00000002
0x0026acb0: 00494379 0026ad78 00000000 002cb362
Backtrace:
=>0 0x00494507 in qspice64 (+0x94507) (0x0026aca0)
1 0x002cb330 (0x002cb2d0)
0x00494507 qspice64+0x94507: movb (%edi), %cl
It goes a bit above my head so, I asked the AI and I got what’s below as a response (the one point I find interesting is that about “delimiters/absolute-value collision” though, I am not implying anything but, maybe someone else spots something helpful/useful):
What the Crash Dump Reveals
-
Crash Instruction (movb (%edi), %cl with EDI = 0x00000000):
The program crashed on an x86 string/byte read (mov cl, byte ptr [edi]) attempting to dereference a NULL pointer. In MSVC-compiled binaries, this pattern is the classic inline expansion of char c = *p; or strlen(p).
-
The Call Chain (0x00494379 $\to$ 0x0049432a $\to$ 0x004943a8 $\to$ 0x004943ca $\to$ 0x00494507):
All return addresses on the stack sit within a tight ~300-byte span. This corresponds to the mutually recursive precedence levels of QSPICE’s recursive-descent expression parser (e.g., parse_or $\to$ parse_xor $\to$ parse_and $\to$ parse_relational).
-
Heap Pointers in the Stack Frame (0x002cb360 and 0x002cb362):
The difference between these two string buffer pointers on the stack is exactly 2 bytes, matching the two-character length of ||. The lexer successfully scanned across the operator, but the pointer returned or stored for the AST/node generator became NULL.
-
Operator Code (EAX = 0x00000002 / [ebp+8] = 0x00000002):
2 is passed directly as the operator/token ID. In logical operator enumerations, 1 is commonly AND, 2 is OR, and 3 is XOR.
Root-Cause Mechanisms
-
Bypassed vs. Entered Precedence Level:
In standard operator precedence, OR is at the outermost (lowest precedence) level of binary logical expressions:
$$\text{OR} \longrightarrow \text{XOR} \longrightarrow \text{AND} \longrightarrow \text{Relational}$$
When an expression contains only &/&& or ^/^^, parse_or() delegates immediately down the chain and never enters its own while (tok == OP_OR) loop. That loop is only entered when the operator is | or ||. Any bug inside that specific handler remains dormant until a pipe operator is parsed.
-
Missing Return / NULL Operator Metadata:
Inside the OP_OR branch of the parser or AST constructor:
- A helper function advancing past
|/|| may lack an explicit return p; on that branch, falling through to return NULL;. The caller immediately attempts to peek at the next character (*p) and page-faults on 0x0.
- Alternatively, an internal operator-symbol table or code-generation dispatch table has
NULL at index 2 (OP_OR). Passing this NULL string to an internal formatter or parser causes an immediate access violation (MSVC’s CRT does not guard against NULL in %s / strlen).
-
Delimiters / Absolute-Value Collision:
If the scanner contains an incomplete or legacy rule treating | as a paired mathematical absolute-value bracket (|x|), encountering x > 1 | y > 1 leaves the absolute-value token unclosed. When it scans to the end of the sub-expression without finding a matching |, it returns NULL without a guard check.
Workarounds
To achieve the exact same logic without triggering the pipe parser bug:
-
Arithmetic OR (cleanest in SPICE):
Boolean relational operators evaluate to 1.0 (true) or 0.0 (false). Adding them behaves as a logical OR:
.func F(x,y) {(x > 1) + (y > 1) > 0 ? 9 : 0}
or with if():
.func F(x,y) {if((x > 1) + (y > 1), 9, 0)}
-
De Morgan’s Law (using working & and !):
$$A \lor B \iff \neg(\neg A \land \neg B)$$
.func F(x,y) {if(!((x <= 1) & (y <= 1)), 9, 0)}
-
Nested if / Ternary (native short-circuiting):
.func F(x,y) {if(x > 1, 9, if(y > 1, 9, 0))}
@VideanuCatalin
I reviewed your post and sent an email to Mike to report this bug.
Update Qspice to verify if you still encounter problem.
Bug is reported through email in Qspice > Help > About Qspice.
10/05/2026 Corrected in error in the .func compiler.