I think there needs to be some invocation beyond inference, so that the user is sure he's meeting the requirements and getting the desired performance.
Chip, how about "CASEN", since it's a 1 of N CASE statement?
JUMP, GOTO, CAST, etc have *VERY* specific connotations in other languages, they should not be overloaded for purposes that are not semantically equal to another language.
If you want to use 2 words to describe it, why not make a modifier for the CASE keyword?
DIRECT CASE
You would modify CASE with DIRECT to indicate it is the DIRECT form of CASE.
Chip, how about "CASEN", since it's a 1 of N CASE statement?
JUMP, GOTO, CAST, etc have *VERY* specific connotations in other languages, they should not be overloaded for purposes that are not semantically equal to another language.
If you want to use 2 words to describe it, why not make a modifier for the CASE keyword?
DIRECT CASE
You would modify CASE with DIRECT to indicate it is the DIRECT form of CASE.
If your compiler needs a directive, then CASE TABLE is clearer than HOTCASE, which sends someone to the manual to figure what that means ?
Do you also want to allow SKIP forms of case, in compact uses ?
I guess one reason I favor just case is that a primary function of any high-level language is to hide the implementation details from the programmer. So I still think a paragraph in the user manual is sufficient to distinguish the semantics between the two case types without messing with the syntax.
@Phil,
A specific variant is required as the programmer is calling for a specific implementation which might not be the preferred compiler method depending on the parameters/cases supplied.
@all,
CASE TABLE x seems to make the most sense to me. “TABLE” is only required to force a specific compiler implementation of the CASE statement. A table will be the result and it will be deterministic.
It’s not a faster or slower implementation as that depends on the various cases (sort of like operands). Same goes for size. So brief/fast/hot or similar do not make sense.
So I vote for NUTCASE
I think there needs to be some invocation beyond inference, so that the user is sure he's meeting the requirements and getting the desired performance.
It turns out that this CASE_FAST (or whatever you'd prefer to think of it as), has the same syntax as CASE, where you can put values and ranges before the ":". The ranges and values must be constants, though. And they must all be within 255 of eachother, though they can be any 32-bit values. And no more than 256 cases, plus one 'other' case for out-of-range/interstitial cases.
Both case and whatever this address jump table method ends up being called are a method of calling 1 of n blocks of code to execute so "select", "choose", "pick", etc. would more logical names for both. If "case" was not already in common use I would have suggested SELE (or SEL=) for what is now case, and SELT for the address table jump.
Here is the code that runs on the chip to make the CASE_FAST work:
'
'
' a: CASE_FAST init
'
' entry:
' x index
' ptra[-1] address
'
' exit:
' x address
'
'
' b: CASE_FAST done
'
' entry:
' x address
'
casefi rflong y 'a get index base
sub x,y 'a zero index
rfword y 'a get index limiter
fle x,y 'a limit index
shl x,#1 'a make into word index
getptr y 'a get rdfast pointer
add x,y 'a add rdfast pointer into word index
rdword a,x 'a read offset word
add a,y 'a add rdfast pointer into offset word
casefd mov a,x '| b get 'done' address
popa x 'a b pop stack
casex add a,pbase '| b add pbase
_ret_ rdfast #0,a 'a b branch to case code
And here is the code in the compiler (minus a bunch of subroutines), which compiles the CASE_FAST structure. I had to replace double-at's with ** to make it display on the forum:
;
;
; Compile block - 'case_fast'
;
cb_case_fast: mov ebp,[column] ;set new column
mov al,type_case_fast ;set new 'case_fast' blocknest
mov ah,case_fast_limit/16+1 ;reserve case_fast_limit + 16 bstack variables
call new_bnest
lea eax,[**comp] ;optimize case_fast block
call optimize_block
call end_bnest ;done, end blocknest
jmp cb_loop ;return to compile block loop
**final_addr = 0
**table_ptr = 1
**source_ptr = 2
**min_value = 3
**max_value = 4
**table_address = 5
**comp: mov eax,**final_addr ;compile final address
call compile_bstack_address
call compile_exp ;compile target value
call get_end
mov al,bc_case_fast_init ;enter case_fast init
call enter_obj
mov eax,0 ;enter spacer for rflong
call enter_obj_long
mov eax,0 ;enter spacer for rfword
call enter_obj_word
mov ebx,[obj_ptr] ;remember jump table start
mov eax,**table_ptr
call write_bstack
mov ebx,[source_ptr] ;remember source ptr
mov eax,**source_ptr
call write_bstack
mov eax,**min_value ;reset min value
mov ebx,7FFFFFFFh
call write_bstack
mov eax,**max_value ;reset max value
mov ebx,80000000h
call write_bstack
mov ecx,0 ;reset case count
mov dl,0 ;reset 'other' block flag
**nextcase1: call get_element ;first pass determines min and max values
jc **done1 ;if eof, first pass done
cmp al,type_end ;ignore blank lines
je **nextcase1
call get_column ;if no indention, first pass done
call back_element
cmp [column],ebp
jbe **done1
cmp dl,1 ;if 'other' already encountered, error
je error_omblc
push ebp ;save original column
mov ebp,[column] ;set new column
cmp al,type_other ;'other' case?
jne **notother1
mov dl,1 ;set 'other' flag
call get_element ;skip 'other'
jmp **getcolon1 ;get colon and skip instruction block
**notother1:
inc ecx ;not 'other', must be case, inc case count
mov eax,ecx
cmp eax,case_fast_limit ;check case_fast limit
ja error_loxcasef
**nextrange1: call get_range ;get value/range and update min and max values
call **updateminmax
mov eax,ebx
call **updateminmax
**notrange1: call check_comma ;if comma, compound case
je **nextrange1
**getcolon1: call get_colon ;get ':' after (last) value/range
call skip_block ;skip instruction block
pop ebp ;restore original column
jmp **nextcase1 ;get next case
**done1:
cmp ecx,0 ;if no value/range cases, error
je error_nce
mov eax,**table_ptr ;write min value into rflong position
call read_bstack
push ebx
mov eax,**min_value
call read_bstack
pop eax
mov [dword obj-6+eax],ebx
push eax ;write max-min+1 value into rfword position
push ebx
mov eax,**max_value
call read_bstack
pop eax
sub ebx,eax
inc ebx
pop eax
mov [word obj-2+eax],bx
mov edx,ecx ;get 'other' case in dx
mov ecx,0 ;init jump table with 'other' case
**inittable: mov [word obj+eax+ecx*2],dx
inc ecx
cmp ecx,ebx
jbe **inittable
shl ecx,1 ;update obj_ptr
add ecx,eax
mov [obj_ptr],ecx
mov eax,**source_ptr ;point back to source after 'case_fast' line
call read_bstack
mov [source_ptr],ebx
mov ecx,0 ;reset case count
**nextcase2: call get_element ;second pass fills in table and compiles blocks
jc **done2 ;if eof, second pass done
cmp al,type_end ;ignore blank lines
je **nextcase2
call get_column ;if no indention, second pass done
call back_element
cmp [column],ebp
jbe **done2
push ebp ;save original column
mov ebp,[column] ;set new column
cmp al,type_other ;'other' case?
jne **notother2
call get_element ;skip 'other'
jmp **getcolon2 ;get colon and compile instruction block
**notother2:
**nextrange2: call get_range ;get value/range and write into jump table
sub ebx,eax ;get range count into ebx
inc ebx
push ebx
push eax ;get table start position into eax
mov eax,**min_value
call read_bstack
pop eax
sub eax,ebx
push eax
mov eax,**table_ptr
call read_bstack
pop eax
shl eax,1
add eax,ebx ;table pointer in eax
pop ebx ;entry count in ebx
**filltable: cmp [word obj+eax],dx ;make sure entries are unclaimed with 'other' case
jne error_cfiinu
mov [word obj+eax],cx ;fill table entries with case number
add eax,2
dec ebx
jnz **filltable
call check_comma ;if comma, compound case
je **nextrange2
**getcolon2: call get_colon ;get ':' after value/range
mov eax,ecx ;write block address for this case
add eax,**table_address
call write_bstack_ptr
call compile_block_check ;compile instruction block
inc ecx ;inc case count
mov eax,ecx ;write block address for potential missing 'other' case
add eax,**table_address ;(points to next/last bc_case_fast_done)
call write_bstack_ptr
mov eax,**table_ptr ;make sure address offset will fit into jump table word
call read_bstack
mov eax,[obj_ptr]
sub eax,ebx
cmp eax,0FFFFh
ja error_cfbex
mov al,bc_case_fast_done ;(case fast done)
call enter_obj
pop ebp ;restore original column
jmp **nextcase2 ;get next case
**done2:
mov eax,**final_addr ;write final address
call write_bstack_ptr
mov eax,**table_ptr ;replace case numbers with block offsets in jump table
call read_bstack
movzx ecx,[word obj-2+ebx] ;get jump table count in ecx
inc ecx
mov edx,ebx ;get jump table offset in edx
**replace: movzx eax,[word obj+ebx] ;get case index from jump table
push ebx
add eax,**table_address ;use case index to look up case block offset
call read_bstack
mov eax,ebx
sub eax,edx
pop ebx
mov [word obj+ebx],ax ;write case block offset into jump table
add ebx,2 ;loop until all cases + 'other' handled
loop **replace
ret
**updateminmax: push eax ;update min and max values with eax
push ebx
push ecx
mov ecx,eax
mov eax,**min_value ;update min value
call read_bstack
cmp ecx,ebx
jge **notmin
mov ebx,ecx
call write_bstack
**notmin:
mov eax,**max_value ;update max value
call read_bstack
cmp ecx,ebx
jle **notmax
mov ebx,ecx
call write_bstack
**notmax:
call read_bstack ;check for span violation
mov ecx,ebx
mov eax,**min_value
call read_bstack
sub ecx,ebx
cmp ecx,255
ja error_cfvmbw
pop ecx
pop ebx
pop eax
ret
This is a real pain in 80386 and it's made worse by this code being recursively called, making it necessary to implement some software stack to augment the limited register set. This would be so much easier to do in Spin2, or any high-level language. In assembly, you've often got to type a lot to make things like conditional sequences, anyway.
x86 is a terrible architecture to begin with. Fun times when you have a multi-GHz CPU with only 6 usable registers (more I guess if you can use al/ah etc. as separate things, but that slows it down a bunch). amd64 at least adds some more registers (as well as guaranteeing SSE2...). ARM and POWER still have twice that. Yikes.
I haven't read the code thoroughly, but is there a particular reason to use a software stack as opposed to the hardware one?
Here is the code that runs on the chip to make the CASE_FAST work:
Do you plan to do a compact CASE that uses DECOD and SKIP opcode(s) ?
It is common for there to be rather less than 256 choices so one that supports up to 32 would be useful, and I think can be smaller again ?
Here is the code that runs on the chip to make the CASE_FAST work:
Do you plan to do a compact CASE that uses DECOD and SKIP opcode(s) ?
It is common for there to be rather less than 256 choices so one that supports up to 32 would be useful, and I think can be smaller again ?
No plans. For contiguous cases, this only uses 1 word per case branch. I don't think it could get any smaller, or faster.
Do you plan to do a compact CASE that uses DECOD and SKIP opcode(s) ?
It is common for there to be rather less than 256 choices so one that supports up to 32 would be useful, and I think can be smaller again ?
No plans. For contiguous cases, this only uses 1 word per case branch. I don't think it could get any smaller, or faster.
There is quite a bit of housekeeping in the table preamble, whilst I expected DECOD and SKIP/SKIPF to largely work in 2~3 lines plus up to 32 jumps. Did I miss something ?
This is a real pain in 80386 and it's made worse by this code being recursively called, making it necessary to implement some software stack to augment the limited register set. This would be so much easier to do in Spin2, or any high-level language. In assembly, you've often got to type a lot to make things like conditional sequences, anyway.
You're planning to port the compiler to Spin2 eventually anyway, aren't you? So why not do that now, and save yourself the trouble of doing all this 80386 programming?
Comments
How would that be?
I think there needs to be some invocation beyond inference, so that the user is sure he's meeting the requirements and getting the desired performance.
JUMP, GOTO, CAST, etc have *VERY* specific connotations in other languages, they should not be overloaded for purposes that are not semantically equal to another language.
If you want to use 2 words to describe it, why not make a modifier for the CASE keyword?
DIRECT CASE
You would modify CASE with DIRECT to indicate it is the DIRECT form of CASE.
How about HOTCASE ?
For awhile, I was thinking briefcase, but that's just too cute, albeit somewhat appropriate.
I still think inference is the better choice, though; although I toyed with, but rejected, nutcase.
-Phil
If your compiler needs a directive, then CASE TABLE is clearer than HOTCASE, which sends someone to the manual to figure what that means ?
Do you also want to allow SKIP forms of case, in compact uses ?
-Phil
It is an ackowledged matter that naming is one of the biggest headaches in programming.
The function itself is more important than the name.
The name can be decided on later.
As long as it's documented I don't really care what it's called.
Well, what I really want to say is BRIEF CASE
Yeah, it's morning, so not at all serious !
which is why I like CASE TABLE x
A specific variant is required as the programmer is calling for a specific implementation which might not be the preferred compiler method depending on the parameters/cases supplied.
@all,
CASE TABLE x seems to make the most sense to me. “TABLE” is only required to force a specific compiler implementation of the CASE statement. A table will be the result and it will be deterministic.
It’s not a faster or slower implementation as that depends on the various cases (sort of like operands). Same goes for size. So brief/fast/hot or similar do not make sense.
So I vote for NUTCASE
CASE FAST is actually pretty good, I think
CASE_FAST x
I wanted to keep the symbol FAST available for user purposes, so the fast case will be CASE_FAST.
I proposed that earlier, but it didn't take.
I like that, too. That name is actually better than CASE.
Both case and whatever this address jump table method ends up being called are a method of calling 1 of n blocks of code to execute so "select", "choose", "pick", etc. would more logical names for both. If "case" was not already in common use I would have suggested SELE (or SEL=) for what is now case, and SELT for the address table jump.
Here is the code that runs on the chip to make the CASE_FAST work:
And here is the code in the compiler (minus a bunch of subroutines), which compiles the CASE_FAST structure. I had to replace double-at's with ** to make it display on the forum:
This is a real pain in 80386 and it's made worse by this code being recursively called, making it necessary to implement some software stack to augment the limited register set. This would be so much easier to do in Spin2, or any high-level language. In assembly, you've often got to type a lot to make things like conditional sequences, anyway.
I haven't read the code thoroughly, but is there a particular reason to use a software stack as opposed to the hardware one?
It is common for there to be rather less than 256 choices so one that supports up to 32 would be useful, and I think can be smaller again ?
No plans. For contiguous cases, this only uses 1 word per case branch. I don't think it could get any smaller, or faster.
There is quite a bit of housekeeping in the table preamble, whilst I expected DECOD and SKIP/SKIPF to largely work in 2~3 lines plus up to 32 jumps. Did I miss something ?
You're planning to port the compiler to Spin2 eventually anyway, aren't you? So why not do that now, and save yourself the trouble of doing all this 80386 programming?