I also use exceptions for control-flow and I feel no shame about it. It's often the best (most concise, most performant, most understandable) solution to a problem, in my eyes.
Most concise: absolutely. Most performant: never. Most understandable: yes, but only if you are cognizant of which call paths can result in an exception and which can’t (i.e. until your code base becomes too large or you’re not the one that wrote it).
Most exceptions, at least in the JVM where this is commonly debated, will incur a penalty of building up the exception and unwinding the call stack. What aspect of performance are you using as an example?
An exception thrown and caught within a compilation unit is effectively a goto. It can translate into nothing more than a jcc instruction, or even nothing at all if it's unconditional. This can be higher performance in practice than threading through a set of Either-style return objects.
You are probably looking for continuations. For many languages exceptions are the nearest approximation.
Personally I have no problem with your approach on the semantic level, but most compiler/interpreter developers consider all exceptional paths as cold as it gets: performance can be "interesting" if you take a lot of them.