You could try chaining two backwards together:
The first time you compute E, you take its output y = E(x) and detach yd = y.detach().requires_grad_()  while making sure detached output requires grad.
When you use the detached output to compute z = E(yd) again, keep grad enabled to keep generating the backward graph.
Freeze the parameters of E, then call gy = z.grad(yd) to get gradients of the final loss w.r.t. detached y.
Finally you can unfreeze E and call y.backward(grad_tensors=gy).
edit: you don’t even actually need to freeze/unfreeze the parameters of E since we’re doing .grad