Skip to content

Commit 412b473

Browse files
committed
initial short circuit eval
1 parent 96efef2 commit 412b473

3 files changed

Lines changed: 168 additions & 21 deletions

File tree

packages/typegpu/src/tgsl/wgslGenerator.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,33 @@ ${this.ctx.pre}}`;
352352
// Logical/Binary/Assignment Expression
353353
const [exprType, lhs, op, rhs] = expression;
354354
const lhsExpr = this._expression(lhs);
355+
356+
// Short Circuit Evaluation
357+
if ((op === '||' || op === '&&') && isKnownAtComptime(lhsExpr)) {
358+
let evalRhs = !lhsExpr.value;
359+
if (op === '&&') {
360+
evalRhs = !evalRhs;
361+
}
362+
363+
if (!evalRhs) {
364+
return op === '||' ? snip(true, bool, 'constant') : snip(false, bool, 'constant');
365+
}
366+
367+
const rhsExpr = this._expression(rhs);
368+
369+
if (isKnownAtComptime(rhsExpr)) {
370+
return snip(rhsExpr.value, bool, 'constant');
371+
}
372+
373+
if (rhsExpr.dataType === UnknownData) {
374+
throw new WgslTypeError(`Right-hand side of '${op}' is of unknown type`);
375+
}
376+
377+
const convRhs = tryConvertSnippet(this.ctx, rhsExpr, bool, false);
378+
const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value;
379+
return snip(rhsStr, bool, 'runtime');
380+
}
381+
355382
const rhsExpr = this._expression(rhs);
356383

357384
if (rhsExpr.value instanceof RefOperator) {

packages/typegpu/tests/std/boolean/not.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,6 @@ describe('not', () => {
123123

124124
expect(tgpu.resolve([f])).toMatchInlineSnapshot(`
125125
"fn f() -> i32 {
126-
if (((false && true) && false)) {
127-
return 1;
128-
}
129126
return -1;
130127
}"
131128
`);

packages/typegpu/tests/tgsl/wgslGenerator.test.ts

Lines changed: 141 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,13 +2137,13 @@ describe('wgslGenerator', () => {
21372137
};
21382138

21392139
expect(tgpu.resolve([f])).toMatchInlineSnapshot(`
2140-
"fn f() -> i32 {
2141-
if ((true || false)) {
2142-
return 1;
2143-
}
2144-
return -1;
2145-
}"
2146-
`);
2140+
"fn f() -> i32 {
2141+
{
2142+
return 1;
2143+
}
2144+
return -1;
2145+
}"
2146+
`);
21472147
});
21482148

21492149
it('handles unary operator `!` on operands from slots and accessors', () => {
@@ -2164,13 +2164,13 @@ describe('wgslGenerator', () => {
21642164
};
21652165

21662166
expect(tgpu.resolve([f])).toMatchInlineSnapshot(`
2167-
"fn f() -> i32 {
2168-
if ((true && true)) {
2169-
return 1;
2170-
}
2171-
return -1;
2172-
}"
2173-
`);
2167+
"fn f() -> i32 {
2168+
{
2169+
return 1;
2170+
}
2171+
return -1;
2172+
}"
2173+
`);
21742174
});
21752175

21762176
it('handles chained unary operators `!`', () => {
@@ -2183,10 +2183,10 @@ describe('wgslGenerator', () => {
21832183
});
21842184

21852185
expect(tgpu.resolve([testFn])).toMatchInlineSnapshot(`
2186-
"fn testFn(n: i32) -> bool {
2187-
return (true || !!!bool(n));
2188-
}"
2189-
`);
2186+
"fn testFn(n: i32) -> bool {
2187+
return true;
2188+
}"
2189+
`);
21902190
});
21912191

21922192
it('handles unary operator `!` on complex comptime-known operand', () => {
@@ -2207,4 +2207,127 @@ describe('wgslGenerator', () => {
22072207
}"
22082208
`);
22092209
});
2210+
2211+
describe('short-circuit evaluation', () => {
2212+
const state = {
2213+
counter: 0,
2214+
result: true,
2215+
};
2216+
2217+
const getTrackedBool = tgpu.comptime(() => {
2218+
state.counter++;
2219+
return state.result;
2220+
});
2221+
2222+
beforeEach(() => {
2223+
state.counter = 0;
2224+
state.result = true;
2225+
});
2226+
2227+
it('handles `||` short-circuit evaluation', () => {
2228+
const f = () => {
2229+
'use gpu';
2230+
let res = -1;
2231+
if (true || getTrackedBool()) {
2232+
res = 1;
2233+
}
2234+
return res;
2235+
};
2236+
2237+
expect(tgpu.resolve([f])).toMatchInlineSnapshot(`
2238+
"fn f() -> i32 {
2239+
var res = -1;
2240+
{
2241+
res = 1i;
2242+
}
2243+
return res;
2244+
}"
2245+
`);
2246+
expect(state.counter).toBe(0);
2247+
});
2248+
2249+
it('handles `&&` short-circuit evaluation', () => {
2250+
const f = () => {
2251+
'use gpu';
2252+
let res = -1;
2253+
if (false && getTrackedBool()) {
2254+
res = 1;
2255+
}
2256+
return res;
2257+
};
2258+
2259+
expect(tgpu.resolve([f])).toMatchInlineSnapshot(`
2260+
"fn f() -> i32 {
2261+
var res = -1;
2262+
return res;
2263+
}"
2264+
`);
2265+
expect(state.counter).toBe(0);
2266+
});
2267+
2268+
it('handles chained `||` short-circuit evaluation', () => {
2269+
state.result = false;
2270+
2271+
const f = () => {
2272+
'use gpu';
2273+
let res = -1;
2274+
if (getTrackedBool() || true || getTrackedBool() || getTrackedBool() || getTrackedBool()) {
2275+
res = 1;
2276+
}
2277+
return res;
2278+
};
2279+
2280+
expect(tgpu.resolve([f])).toMatchInlineSnapshot(`
2281+
"fn f() -> i32 {
2282+
var res = -1;
2283+
{
2284+
res = 1i;
2285+
}
2286+
return res;
2287+
}"
2288+
`);
2289+
expect(state.counter).toEqual(1);
2290+
});
2291+
2292+
it('handles chained `&&` short-circuit evaluation', () => {
2293+
const f = () => {
2294+
'use gpu';
2295+
let res = -1;
2296+
if (getTrackedBool() && false && getTrackedBool() && getTrackedBool() && getTrackedBool()) {
2297+
res = 1;
2298+
}
2299+
return res;
2300+
};
2301+
2302+
expect(tgpu.resolve([f])).toMatchInlineSnapshot(`
2303+
"fn f() -> i32 {
2304+
var res = -1;
2305+
return res;
2306+
}"
2307+
`);
2308+
expect(state.counter).toBe(1);
2309+
});
2310+
2311+
it('handles mixed logical operators short-circuit evaluation', () => {
2312+
const f = () => {
2313+
'use gpu';
2314+
let res = -1;
2315+
if (true || (getTrackedBool() && getTrackedBool())) {
2316+
res = 1;
2317+
}
2318+
return res;
2319+
};
2320+
2321+
expect(tgpu.resolve([f])).toMatchInlineSnapshot(`
2322+
"fn f() -> i32 {
2323+
var res = -1;
2324+
{
2325+
res = 1i;
2326+
}
2327+
return res;
2328+
}"
2329+
`);
2330+
expect(state.counter).toBe(0);
2331+
});
2332+
});
22102333
});

0 commit comments

Comments
 (0)