Skip to content

How to implement delegate's target #3497

Closed
@aheejin

Description

@aheejin

The following is the new instruction delegate's semantics, taken from my LLVM CL's comment:

Linearizing the control flow by placing TRY / END_TRY markers can create
mismatches in unwind destinations for throwing instructions, such as calls.

We use the 'delegate' instruction to fix the unwind mismatches. 'delegate'
instruction delegates an exception to an outer 'catch'. It can target not
only 'catch' but all block-like structures including another 'delegate',
but with slightly different semantics than branches. When it targets a
'catch', it will delegate the exception to that catch. It is being
discussed how to define the semantics when 'delegate''s target is a non-try
block: it will either be a validation failure or it will target the next
outer try-catch. But anyway our LLVM backend currently does not generate
such code. The example below illustrates where the 'delegate' instruction
in the middle will delegate the exception to, depending on the value of N.
try
  try
    block
      try
        try
          call @foo
        delegate N    ;; Where will this delegate to?
      catch           ;; N == 0
      end
    end               ;; N == 1 (invalid; will not be generated)
  delegate            ;; N == 2
catch                 ;; N == 3
end
                      ;; N == 4 (to caller)

So delegate's immediate field can target all control flow structures including blocks and loops, like brs. But if the target is block or loop, it will be a validation failure. When it targets a try, it is really targetting not try but its catch.

Then here comes the catch: Currently if and try do not take label names; they use wrapping blocks or inner blocks to achieve the same semantics, mostly because there are too many places to handle if we add a new control flow structure that can have a label. But if we want to give delegate a label, it should target a try (more precisely its catch), and if it targets a block, it will be a validation failure. But I'm not sure if we want to give try a label and add its handling in a bunch of places, which is exactly what we wanted to avoid when we implemented if and try this way.

We are already doing some ugly things to implement try-catch.

  1. We create an inner block within catch, but we assume that block is removed when we print the text/binary. The reason it should be removed is catch body should start with a pop instruction (or a value on the stack in binary), and block should not interfere in between. This is easily done when there's no branch that targets the inner block.
  2. When there is br 0 within a catch, we wrap the whole try-catch with a block and make the br target it, not the inner block within catch, for the reason I described in 1. (Fix inner block problem with 'catch' #3129)

We can probably do a similar thing for delegate's target... Like, wrap the target try-catch with a block and make delegate target that instead, and we delete that block when we write the binary/text...? But unlike the wrapping block we created for br, delegate targetting a block is a validation failure and it must be removed when we write that out. Also in this case we should maintain the relationship between the wrapping block and try for the whole optimization pipeline, i.e., no other instruction should interfere between them and the block should not be removed.

Not sure what is the cleaniest way. Any ideas?
@kripken @tlively

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions