Jumptable to labels

Just starting out? Need help? Post your questions and find answers here.
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Jumptable to labels

Post by Josh »

I know how to create a jumptable to procedures. But if I want to create a jumptable to labels, then I have, as far as I know, to use assambler. Unfortunately, I have very little idea of ASM and created the following example:

Code: Select all

Procedure Jump (x)

  DataSection
    DataSta:
  
    Data.i ?Label_000
    Data.i ?Label_001
    Data.i ?Label_002
    Data.i ?Label_003
    Data.i ?Label_004
    Data.i ?Label_005

  EndDataSection

  *AdrLabel = PeekI (?DataSta + x * SizeOf (Integer))
  !jmp dword [p.p_AdrLabel]
  LabelBack:

  ;Some other code

  ProcedureReturn

  Label_000: : Debug "X = 000" : Goto LabelBack
  Label_001: : Debug "X = 001" : Goto LabelBack
  Label_002: : Debug "X = 002" : Goto LabelBack
  Label_003: : Debug "X = 003" : Goto LabelBack
  Label_004: : Debug "X = 004" : Goto LabelBack
  Label_005: : Debug "X = 005" : Goto LabelBack

EndProcedure


Jump (3)
Now my questions:
- Is the ASM-code correct?
- Is the same ASM-code running on linux and osx too?
- Must 'dword ' be changed for x64?

Thxs
Josh
sorry for my bad english
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Jumptable to labels

Post by Keya »

my jump table example is for labels within a procedure, hope it helps - http://www.purebasic.fr/english/viewtop ... 12&t=67044
- Is the same ASM-code running on linux and osx too?
you should get yourself a free copy of VirtualBox and a free copy of Linux Mint and try for yourself! I would never trust that asm code runs just by looking at it hehe. (if you're asking "can the same asm code run on both Windows and Linux/Mac", the answer is generally yes)
Must 'dword ' be changed for x64?
You can still use dword mods in x64, ie you can "mov dword [rax], $11223344", but that's modifying the contents at an address - when dealing with the address directly ie JMP'ing to it yes that needs to be a qword on x64
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: Jumptable to labels

Post by Josh »

Keya wrote:my jump table example is for labels within a procedure, hope it helps - http://www.purebasic.fr/english/viewtop ... 12&t=67044
My ASM-Line was stolen from this topic :D

Thxs for your hints
sorry for my bad english
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Jumptable to labels

Post by Keya »

Josh wrote:But if I want to create a jumptable to labels, then I have, as far as I know, to use assambler.
btw there is also the GOTO statement, which is basically just a JMP.

Code: Select all

!int 3
Goto MyLabel
!nop
MyLabel:
!inc eax

Code: Select all

0040103F  |.  CD 03           int 3
00401041  |.  EB 01           jmp short 00401044
00401043  |   90              nop
00401044  |>  40              inc eax
Some people argue dogmatically that you should never use GOTO statements (and yes you have to be careful when using them and yes there are usually but not necessarily always better methods), that's already been beaten to death at this and every other programming forum, but to them I say they should never use an app with a JMP instruction :D #1stworldproblems
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: Jumptable to labels

Post by Josh »

Poaaahhhh, the old GOTO discussion. Sure, you can make mistakes, but that's the same with each other code.

In this case here, GOTO doesn't help me. Pb's GOTO needs a fix label but I need an address to be flexible.
sorry for my bad english
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Jumptable to labels

Post by Keya »

unfortunately yes. I've already feature-requested the ability to "GOTO <integer>", fairly simple one to implement but has probably already been lost/forgotten among the million other requests! http://www.purebasic.fr/english/viewtop ... =3&t=67058
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: Jumptable to labels

Post by Josh »

Good idea. I voted for it.
sorry for my bad english
User avatar
idle
Always Here
Always Here
Posts: 5840
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Jumptable to labels

Post by idle »

use enableasm and it'll work on x86 and x64

Code: Select all

Procedure Jump (x)

  DataSection
    DataSta:
  
    Data.i ?Label_000
    Data.i ?Label_001
    Data.i ?Label_002
    Data.i ?Label_003
    Data.i ?Label_004
    Data.i ?Label_005

  EndDataSection

  *AdrLabel = PeekI (?DataSta + x * SizeOf (Integer))
  EnableASM
  jmp *AdrLabel
  DisableASM
  LabelBack:

  ;Some other code

  ProcedureReturn

  Label_000: : Debug "X = 000" : Goto LabelBack
  Label_001: : Debug "X = 001" : Goto LabelBack
  Label_002: : Debug "X = 002" : Goto LabelBack
  Label_003: : Debug "X = 003" : Goto LabelBack
  Label_004: : Debug "X = 004" : Goto LabelBack
  Label_005: : Debug "X = 005" : Goto LabelBack

EndProcedure


Jump (3)
Windows 11, Manjaro, Raspberry Pi OS
Image
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Jumptable to labels

Post by davido »

I don't know how relevant this is but I did the following two tests:

1. idle's 'Label' code:

Code: Select all

Procedure Jump (x)

  DataSection
    DataSta:
  
    Data.i ?Label_000
    Data.i ?Label_001
    Data.i ?Label_002
    Data.i ?Label_003
    Data.i ?Label_004
    Data.i ?Label_005

  EndDataSection

  *AdrLabel = PeekI (?DataSta + x * SizeOf (Integer))
  EnableASM
  jmp *AdrLabel
  DisableASM
  LabelBack:

  ;Some other code

  ProcedureReturn

  Label_000: : Debug "X = 000" : Goto LabelBack
  Label_001: : Debug "X = 001" : Goto LabelBack
  Label_002: : Debug "X = 002" : Goto LabelBack
  Label_003: : Debug "X = 003" : Goto LabelBack
  Label_004: : Debug "X = 004" : Goto LabelBack
  Label_005: : Debug "X = 005" : Goto LabelBack

EndProcedure

dt = ElapsedMilliseconds()
For M = 1 To 250000000
  Jump (5)
Next M

MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
2. Calling a Procedure:

Code: Select all

dt = ElapsedMilliseconds()


Procedure Test(x.i)
  
  
EndProcedure



dt = ElapsedMilliseconds()
For M = 1 To 250000000
  Test(3)
Next M

MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
Results (i7 5960; Windows 10; PureBasic 5.50 x64)

Labels: 250,000,000 in 1 second.
Procedure: 250,000,000 in 0.5 second.
DE AA EB
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Jumptable to labels

Post by Keya »

davido, not a fair test because this is about one simple thing which isn't what you're testing for - replacing a Case or If table with a single jump. On top of that, all your Proc test does is call a Proc - but your Jumptable test calls a Proc, and then does work inside the proc - of course that's going to be slower!

ie. rather than...

Code: Select all

If val = #DO_ADD ;cmp
 ;je/jmp to add handler
ElseIf val = #DO_SUB ;cmp
 ;je/jmp to sub handler
ElseIf val = #DO_MUL ;cmp
 ;je/jmp to mul handler
... etc etc...
- there could be dozens, hundreds even, and every If/ElseIf statement must be tested individually. (And if for example you're doing this for every channel in every pixel in every image and you're processing multiple images it quickly adds up)

So the jump table does away with the multiple comparison tests, instead simply jmp'ing to exactly where it needs to go -- dozens of "cmp, je address" statements are reduced to a single "jmp address". And you don't need a timer to know that one "jmp address" is going to be faster than even a single "cmp + jmp" :)
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Jumptable to labels

Post by davido »

@Keya,
Thank you for the explanation. :D
DE AA EB
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: Jumptable to labels

Post by Josh »

This is the same code as in my first posting. The once difference, I set *AdrLabel to 'Static' and get an Error. With 'Define' and 'Protected' it works fine. Why?

Code: Select all

Procedure Jump (x)
  Static *AdrLabel

  DataSection
    DataSta:
  
    Data.i ?Label_000
    Data.i ?Label_001
    Data.i ?Label_002
    Data.i ?Label_003
    Data.i ?Label_004
    Data.i ?Label_005

  EndDataSection

  *AdrLabel = PeekI (?DataSta + x * SizeOf (Integer))
  !jmp dword [p.p_AdrLabel]
  LabelBack:

  ;Some other code

  ProcedureReturn

  Label_000: : Debug "X = 000" : Goto LabelBack
  Label_001: : Debug "X = 001" : Goto LabelBack
  Label_002: : Debug "X = 002" : Goto LabelBack
  Label_003: : Debug "X = 003" : Goto LabelBack
  Label_004: : Debug "X = 004" : Goto LabelBack
  Label_005: : Debug "X = 005" : Goto LabelBack

EndProcedure


Jump (3)
sorry for my bad english
User avatar
mk-soft
Always Here
Always Here
Posts: 6209
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Jumptable to labels

Post by mk-soft »

Another way with virtual table
Is faster than 'goto'

Code: Select all


Prototype protoInvoke(*Args = 0)

Structure udtInvoke
  *fc.protoInvoke[0]
EndStructure

Procedure Prog0(*Args)
  Debug "Prog 0"
EndProcedure

Procedure Prog1(*Args)
  Debug "Prog 1"
EndProcedure

Procedure Prog2(*Args)
  Debug "Prog 2"
EndProcedure

Procedure Prog3(*Args)
  Debug "Prog 3"
EndProcedure

Procedure Prog4(*Args)
  Debug "Prog 4"
EndProcedure

DataSection
  VT_Prog:
  Data.i @Prog0()
  Data.i @Prog1()
  Data.i @Prog2()
  Data.i @Prog3()
  Data.i @Prog4()
EndDataSection

Global *Call.udtInvoke
*call = ?VT_Prog
*call\fc[1]()
index = 3
*call\fc[index]()

dt = ElapsedMilliseconds()
For M = 1 To 250000000
  *call\fc[3]()
Next M

MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: Jumptable to labels

Post by Josh »

mk-soft wrote:Another way with virtual table
Is faster than 'goto'
The problem is, I have some hundreds of possibilities, about 50% have to do nothing, about 48% have one command line and only very few have more than 5 command lines. So I don't want to create some hundreds procedures.

Btw, it isn't faster than 'goto' :D

Code: Select all

Prototype protoInvoke(*Args = 0)

Structure udtInvoke
  *fc.protoInvoke[0]
EndStructure

Structure INTEGERARRAY
  i.i[0]
EndStructure

Procedure Prog0(*Args)
  Debug "Prog 0"
EndProcedure

Procedure Prog1(*Args)
  Debug "Prog 1"
EndProcedure

Procedure Prog2(*Args)
  Debug "Prog 2"
EndProcedure

Procedure Prog3(*Args)
  Debug "Prog 3"
EndProcedure

Procedure Prog4(*Args)
  Debug "Prog 4"
EndProcedure

DataSection

  VT_Prog:
  Data.i @Prog0()
  Data.i @Prog1()
  Data.i @Prog2()
  Data.i @Prog3()
  Data.i @Prog4()

  VT_Label:
  Data.i ?Label0
  Data.i ?Label1
  Data.i ?Label2
  Data.i ?Label3
  Data.i ?Label4
 
EndDataSection

Global *Call.udtInvoke
*call = ?VT_Prog
*call\fc[1]()
index = 3
*call\fc[index]()

dt = ElapsedMilliseconds()

For M = 1 To 250000000
  *call\fc[3]()
Next M

MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")

; -------------------------------------

Define *Addresses.INTEGERARRAY
*Addresses = ?VT_Label
EnableASM

dt = ElapsedMilliseconds()

For M = 1 To 250000000
  jmp *Addresses\i[4]
  LabelRet:
Next M

MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
DisableASM

; -------------------------------------

End

Label0: : Debug "Label 0" : Goto LabelRet
Label1: : Debug "Label 1" : Goto LabelRet
Label2: : Debug "Label 2" : Goto LabelRet
Label3: : Debug "Label 3" : Goto LabelRet
Label4: : Debug "Label 4" : Goto LabelRet
sorry for my bad english
User avatar
mk-soft
Always Here
Always Here
Posts: 6209
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Jumptable to labels

Post by mk-soft »

Ok. Goto is a faster, but you need all labels too.
With VT can you defined the right functions.

Code: Select all


Prototype protoInvoke(*Args = 0)

Structure udtInvoke
  *fc.protoInvoke[0]
EndStructure

Procedure ProgNothing(*Args)
  Debug "Do Nothing"
EndProcedure

Procedure Prog0(*Args)
  Debug "Prog 0"
EndProcedure

Procedure Prog1(*Args)
  Debug "Prog 1"
EndProcedure

Procedure Prog5and6(*Args)
  Debug "Prog 5 and 6"
EndProcedure

Procedure Prog7(*Args)
  Debug "Prog 7"
EndProcedure

DataSection
  VT_Prog:
  Data.i @Prog0()
  Data.i @Prog1()
  Data.i @ProgNothing()
  Data.i @ProgNothing()
  Data.i @ProgNothing()
  Data.i @Prog5and6()
  Data.i @Prog5and6()
  Data.i @Prog7()
EndDataSection

Global *Call.udtInvoke = ?VT_Prog
dt = ElapsedMilliseconds()
For M = 1 To 250000000
  *call\fc[5]()
Next M

MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
P.S.
I use the same method for my EventDesigner to manage all Events (EventMain.pb)
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply