Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace skiz by if_then_call #84

Closed
wants to merge 3 commits into from
Closed

replace skiz by if_then_call #84

wants to merge 3 commits into from

Conversation

jan-ferdinand
Copy link
Member

@jan-ferdinand jan-ferdinand commented Oct 12, 2022

As discussed in this issue, implementing a proper “if then else” is not currently very straightforward using Triton assembly. The new instruction if_then_call alleviates the problem.

For instruction if_then_call, note the difference between the potential offsets added to instruction pointer ip and jump origin o. Specifically, if the top of the stack a is 0, the instruction pointer ip is incremented by 2, skipping the instruction's argument d. However, if the top of the stack a is not 0, the return address put on the jump stack is ip+4. This skips not only the instruction's argument d, but also the next two instructions when returning. Together, this mechanic allows following instruction if_then_call + d with instruction call + e to serve as the “else” branch. If no “else” branch is desired, instruction if_then_else + d can be followed with two instructions nop. Of course, instruction if_then_call can be followed by any instruction. The appropriate care should be taken to return only to where it makes sense.

We might drop instruction skiz from the ISA, but the combination of skiz recurse return is beautiful.

@jan-ferdinand
Copy link
Member Author

jan-ferdinand commented Oct 12, 2022

Expressing skiz using if_then_call:

push 0 eq
if_then_call dummy-label
instruction_to_maybe_skip
nop_or_argument

dummy-label: return

Program length: 8. Execution cycle count: 4 or 5.

Note that the emulated skiz, unlike the currently implemented version, does not pop the top of the stack. Instead, the top of the stack is modified. The new value is 1 if the old value was 0, and 0 otherwise

$$\mathsf{st0}' = \begin{cases}1, &\text{ if }\mathsf{st0}= 0 \\ 0,&\text{ otherwise}\end{cases}$$

The technical reason is that any one instruction may change at most one memory pointer. Since if_then_call might change the jump stack pointer jsp, the length of the operational stack cannot change at the same time, because that would modify osp.

Of course, depending on instruction_to_maybe_skip, the top of the stack might have been popped. For example, somewhat preserving the beauty I see in skiz recurse return:

push 0 eq
if_then_call dummy-label
pop recurse
return

dummy-label: pop return

@jan-ferdinand jan-ferdinand changed the title add instruction “if_then_call” replace skiz by if_then_call Oct 12, 2022
@aszepieniec
Copy link
Collaborator

aszepieniec commented Oct 13, 2022

I thought about emulating if-then-else with skiz, and came up with a way to do that. If I understand correctly, the limitation was that the two branches could not leave the stack in an arbitrary state. This construction alleviates that limitation, with no change to the instruction set or arithmetization.

dup0
skiz
call if-wrapper
call else-wrapper

if-wrapper:
pop
call if-branch
push 1
return

else-wrapper:
push 0 eq # logical not
skiz
call else-branch
return

To verify the execution, see the following table:

instruction stack stack
dup0 _ 0 _ 1
skiz _ 0 0 _ 1 1
call if-wrapper _ 1
pop _ 1
call if-branch _
... return &
push 1 &
return & 1
call else-wrapper _ 0 & 1
push 0 eq _ 0 & 1
skiz _ 1 & 0
call else-branch _
... return %
return % &

@jan-ferdinand
Copy link
Member Author

Very elegant. This warrants a more thorough analysis of the costs of the two instructions skiz and if_then_call.

@sshine
Copy link
Collaborator

sshine commented Jan 2, 2023

A variation of this if-then-else wrapper that I derived is like this:

// Before: _ c
// After: _ ...
some-routine:
  push 1 // -> _ c 1
  swap1  // -> _ 1 c
  skiz   // -> _ 1
    call then-branch
  skiz   // -> _
    call else-branch
  ...
  return

// Before: _ 1
// After: _ ... 0
then-branch:
  pop
  ...
  push 0
  return

// Before: _
// After: _ ...
else-branch:
  ...
  return

I.e., start by pushing a 1 that will make the else-skiz trigger, and in the then-branch change that 1 to a 0.

A limitation of calling to a conditional branch is that the branch cannot return all the way back.

@aszepieniec
Copy link
Collaborator

A limitation of calling to a conditional branch is that the branch cannot return all the way back.

I don't follow. Please elaborate.

@sshine
Copy link
Collaborator

sshine commented Jan 4, 2023

A limitation of calling to a conditional branch is that the branch cannot return all the way back.

I don't follow. Please elaborate.

What I meant was just: The return instruction can only jump one call back, and conditionals with bodies of more than one instruction must likely call.

So if you want to leave a function entirely from within a skiz call branch, you can leave a boolean on the stack (similar to the if-then-else construction) and skiz return immediately after returning from the branch, e.g.:

// Before: _ c
// After: _ (...x|...y)
foo:
    push 0 // _ c 0
    swap1  // _ 0 c
    skiz   // _ 0
        call foo-then // _ ...x 1
    skiz   // _ ...x
        return
    ...y   // _ ...y
    return // leave 'foo'

// Before: _ 0
// After: _ ...x 1
foo-then:
    pop    // _
    ...x   // _ ...x
    push 1 // _ ...x 1
    return // return to 'foo', does not leave 'foo'

For nested conditionals where the inner branch leaves the function, applying this trick once for each nesting is necessary.

@sshine sshine mentioned this pull request Jan 12, 2023
@jan-ferdinand
Copy link
Member Author

There seems to be no immediate need to replace instruction skiz.

@jan-ferdinand jan-ferdinand deleted the if_then_call branch August 9, 2023 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants