This only calls `noteStep()` on jump opcodes, so this works for loops.
It does not prevent "hangs" when a function is just _very_ long (could
be interesting how this interfaces with expand statements?).
Fixes https://github.com/llvm/llvm-project/issues/165951
Set the EvalMode on InterpState and abort when initalizing a global
temporary, like the current interpreter does. The rest is just plumbing
in EvaluateAsLValue.
Fixes#157497
Instead of relaying from InterpState to the parent state (which is an
EvalInfo), just save the variables in State instead, so both subclasses
have access to it.
Due to all the tracking via map(s) and a BumpPtrAllocator, the creating
and destroying the DynamicAllocator is rather expensive. Try to do it
lazily and only create it when first calling
InterpState::getAllocator().
This has been a long-standing problem, but we didn't use to call the
destructors of items on the stack unless we explicitly `pop()` or
`discard()` them.
When interpretation was interrupted midway-through (because something
failed), we left `Pointer`s on the stack. Since all `Block`s track what
`Pointer`s point to them (via a doubly-linked list in the `Pointer`),
that meant we potentially leave deallocated pointers in that list. We
used to work around this by removing the `Pointer` from the list before
deallocating the block.
However, we now want to track pointers to global blocks as well, which
poses a problem since the blocks are never deallocated and thus those
pointers are always left dangling.
I've tried a few different approaches to fixing this but in the end I
just gave up on the idea of never knowing what items are in the stack.
We already have an `ItemTypes` vector that we use for debugging
assertions. This patch simply enables this vector unconditionally and
uses it in the abort case to properly `discard()` all elements from the
stack. That's a little sad IMO but I don't know of another way of
solving this problem.
As expected, this is a slight hit to compile times:
https://llvm-compile-time-tracker.com/compare.php?from=574d0a92060bf4808776b7a0239ffe91a092b15d&to=0317105f559093cfb909bfb01857a6b837991940&stat=instructions:u
This way, we can check a single uint8_t for != 0 to know whether this
block is accessible or not. If not, we still need to figure out why not
and diagnose appropriately of course.
This fixes the edge case we had with variables pointing to dynamic
blocks, which forced us to convert basically *all* dynamic blocks to
DeadBlock when deallocating them.
We now don't run dynamic blocks through InterpState::deallocate() but
instead add them to a DeadAllocations list when they are deallocated but
still have pointers.
As a consequence, not all blocks with Block::IsDead = true are
DeadBlocks.
First, the old MoveFn was rather inefficient, since the dead data cannot
ever be accessed anyway.
Second, there was a problem where the only reason a block still had a
pointer to it (and thus was made into a DeadBlock instead of simply
being deallocated) as that a nested field in the block pointed to the
block itself.
Fix this by calling the dtor function unconditionally. If the block
*still* has pointers after that, we really need to create a DeadBlock
for it.
Instead of heap-allocating it. This is similar to what the current
interpeter does. In C, we have no function calls, so the extra heap
allocation never makes sense.
And fix the diagnostics for __builtin_is_constant_evaluated(). We can be
in a non-constant context, but calling an immediate function always
makes the context constant for the duration of that call.