Recent

Author Topic: how to break out early of a case ?  (Read 80674 times)

440bx

  • Hero Member
  • *****
  • Posts: 5569
Re: how to break out early of a case ?
« Reply #210 on: May 29, 2025, 11:19:42 pm »
A goto would be good enough for those cases. Fallthrough is not something brilliant. It is simply C showing its macro assembler pedigree where
AFAIC, in applications programming gotos are _always_ unacceptable.  Some goto "flavors" are acceptable in systems programming and, even then, the cases are few and far between.

In applications programming, if a goto is needed, there are only two possibilities: 1. the language has shortcomings it should not have.  and/or 2. the programmer wrote deficient code.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12304
  • FPC developer.
Re: how to break out early of a case ?
« Reply #211 on: May 29, 2025, 11:26:49 pm »
A goto would be good enough for those cases. Fallthrough is not something brilliant. It is simply C showing its macro assembler pedigree where
AFAIC, in applications programming gotos are _always_ unacceptable.  Some goto "flavors" are acceptable in systems programming and, even then, the cases are few and far between.

IMHO it is similarly bad as fall through. Just like those few cases where fallthrough is useful for something other than summing up multiple cases. And since those are already such an exceptional case (and probably lowlevel performance related), it is acceptable to use GOTO IMHO.

I don't have a single GOTO in a case in my code. But I also don't have any uses for nonstandard case fallthrough in my C code. (which are, admitted, about a factor 10-20 smaller than my Pascal codebases)
 
Quote
In applications programming, if a goto is needed, there are only two possibilities: 1. the language has shortcomings it should not have.  and/or 2. the programmer wrote deficient code.

I always was a bit of an outlier there, and to me such things were always too absolute, unfortunately like many dogmas in programming.

I can remember asking some professor about it (I think it was a numeric math class, which was then still done in Pascal) and he answered "you can use as many goto's as you like as long as you submit a 2 page essay per use about why you really need it".

I always stuck to that. Enough to be a deterrent, not enough to be some absolute decree.
« Last Edit: June 02, 2025, 02:19:08 pm by marcov »

munair

  • Hero Member
  • *****
  • Posts: 884
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: how to break out early of a case ?
« Reply #212 on: June 01, 2025, 05:55:45 pm »
It is perfectly acceptable to have specific directives for a switch statement, like fall, next and done, as I demonstrated earlier, which are basically specific 'goto' statements restricted to the switch statement. One can argue that such directives are hardly if ever needed, but programming tasks for e.g. state machines can benefit from them. But no matter the task, implementing these directives is easy and adds only minimal code.
« Last Edit: June 01, 2025, 10:00:17 pm by munair »
It's only logical.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12304
  • FPC developer.
Re: how to break out early of a case ?
« Reply #213 on: June 08, 2025, 03:07:44 pm »
I splitted the non CASE related part of the discussion off as " Functional if"  thread.

440bx

  • Hero Member
  • *****
  • Posts: 5569
Re: how to break out early of a case ?
« Reply #214 on: June 08, 2025, 03:08:42 pm »
I splitted the non CASE related part of the discussion off as " Functional if"  thread.
Thank you Marco.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

munair

  • Hero Member
  • *****
  • Posts: 884
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: how to break out early of a case ?
« Reply #215 on: June 09, 2025, 11:21:49 am »
In applications programming, if a goto is needed, there are only two possibilities: 1. the language has shortcomings it should not have.  and/or 2. the programmer wrote deficient code.

I always was a bit of an outlier there, and to me such things were always too absolute, unfortunately like many dogmas in programming.

I can remember asking some professor about it (I think it was a numeric math class, which was then still done in Pascal) and he answered "you can use as many goto's as you like as long as you submit a 2 page essay per use about why you really need it".

I always stuck to that. Enough to be a deterrent, not enough to be some absolute decree.

I can remember using goto in nested for loops to break out of them in one jump. Sure, this can be coded without goto by exiting the nested loops in a separate procedure (which might require passing parameters) or some other construct to avoid goto. Point being, there are cases where goto is legit and a cleaner solution and I would offer one sentence rather than 2 pages to explain why.  :D
It's only logical.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12304
  • FPC developer.
Re: how to break out early of a case ?
« Reply #216 on: June 09, 2025, 11:50:58 am »
I can remember using goto in nested for loops to break out of them in one jump. Sure, this can be coded without goto by exiting the nested loops in a separate procedure (which might require passing parameters) or some other construct to avoid goto. Point being, there are cases where goto is legit and a cleaner solution and I would offer one sentence rather than 2 pages to explain why.  :D

I have 4 in my 500 kloc, and they are always breaking out of a while in heavily calculating code. Some may be replaced by break nowadays I guess.

440bx

  • Hero Member
  • *****
  • Posts: 5569
Re: how to break out early of a case ?
« Reply #217 on: June 09, 2025, 01:15:18 pm »
I feel that "goto" is just the easy way out which sooner or later is extremely likely to create problems.  Open the door to "goto" and that's opening the door to spaghetti code.

For instance, in this FSM:
Code: Pascal  [Select][+][-]
  1. while TRUE do
  2. begin
  3.   case SelectorVariable of
  4.     1 :
  5.     begin
  6.       <statement>
  7.       <statement>
  8.  
  9.       if somecondition then break;  { exits the case early }
  10.  
  11.       <statement>
  12.  
  13.       SelectorVariable := 2; { to cause fall through as in C }
  14.     end;
  15.  
  16.     2 :  
  17.     begin
  18.       <bunch of statements here>
  19.  
  20.       break; { exit case statement }
  21.     end;
  22.  
  23.     <more cases if needed >
  24.  
  25.     otherwise
  26.     begin
  27.       <statements>
  28.  
  29.       break;
  30.     end;
  31.   end; { case }
  32. end; { while }
  33.  
execution can go just about anywhere and, replicating the possibilities would require a bunch of labels and "goto"(s).  I really believe the FSM is a significantly superior solution.  The problem is, "goto" is the "instant solution" whereas, it took me some time for that FSM to come to mind.  Also, I like solving problems and always goto-free code is an ever-present problem I like to find simple solutions for.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

creaothceann

  • Full Member
  • ***
  • Posts: 140
Re: how to break out early of a case ?
« Reply #218 on: June 11, 2025, 07:43:10 pm »
C's fall-through behavior is nice, in the sense that it has zero performance impact. It's simply what the CPU does under the hood by default. Nevertheless I'd want a keyword for that, and a jump behind the last case as the default behavior.

Putting the entire case..end block into a repeat..until False; block is quite silly - the only benefit is the indentation. A goto is a much clearer sign of intent, but also feels unwieldy because you have to go to the top of the function and declare the label first...

Even worse is putting the entire case..end block into its own subroutine (just so you can use exit in a misguided attempt to avoid goto), as it breaks the flow and impacts performance (FP likes to add SEH code everywhere) unless the subroutine is inlined. (But afaik the subroutine is always added to the compiled program even if all calls to the subroutine are inlined, would love to know if that's wrong.)

case could be much better:
  • an option to force the compiler to use a jump table (instead of repeated ifs)
  • an option to skip checking if the selector is not one of the cases
  • an option to terminate each case with a RET instruction for exiting the current subroutine instead of jumping behind the last case (RET uses the CPU's return stack buffer and doesn't pollute the branch predictor with unnecessary jumps)
  • a way to reduce the jump table entries to smaller (16-bit or even 8-bit) displacements if the cases are close enough together
  • a way to jump from one case to another case (determined by a value loaded at runtime) without going back to the top (would help the CPU's branch predictor because there's no longer a single dispatch point)
This would help a lot with making state machines (e.g. bytecode interpreters) faster...

MarkMLl

  • Hero Member
  • *****
  • Posts: 8453
Re: how to break out early of a case ?
« Reply #219 on: June 12, 2025, 09:01:04 am »
an option to force the compiler to use a jump table (instead of repeated ifs)

Already does that.

Quote
an option to skip checking if the selector is not one of the cases

Else/otherwise is already optional (whether this is good or bad is arguable, but that's the way the language is).

Quote
a way to jump from one case to another case (determined by a value loaded at runtime) without going back to the top (would help the CPU's branch predictor because there's no longer a single dispatch point)

This would help a lot with making state machines (e.g. bytecode interpreters) faster...

That I agree with. In fact I think it would be defensible to deprecate all use of goto except for this specific purpose, and to unify selectors and labels which after all are already visually indistinguishable.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

creaothceann

  • Full Member
  • ***
  • Posts: 140
Re: how to break out early of a case ?
« Reply #220 on: June 12, 2025, 02:39:09 pm »
an option to force the compiler to use a jump table (instead of repeated ifs)
Already does that.
Afaik only if there are >= 12 cases.


Quote
an option to skip checking if the selector is not one of the cases
Else/otherwise is already optional (whether this is good or bad is arguable, but that's the way the language is)
The compiler still adds a check (the "cmpl $11,%edi; ja .Lj6" in the link above). If you have the full set of 256 cases for a bytecode interpreter it thankfully removes the check, but still ANDs the parameter with 255; there's apparently no way to tell the compiler that the passed value cannot be greater. Sure, it's a tiny issue, but an emulator literally executes this code millions of times per second.

(And the compiler doesn't realize that one case has to be picked and every case sets the return value, so it warns that the function result doesn't seem to be initialized...)

PascalDragon

  • Hero Member
  • *****
  • Posts: 6033
  • Compiler Developer
Re: how to break out early of a case ?
« Reply #221 on: June 13, 2025, 11:16:33 pm »
an option to force the compiler to use a jump table (instead of repeated ifs)
Already does that.
Afaik only if there are >= 12 cases.

It's not so simple as it highly depends on the platform and the case-statement in question, see here.

  • an option to terminate each case with a RET instruction for exiting the current subroutine instead of jumping behind the last case (RET uses the CPU's return stack buffer and doesn't pollute the branch predictor with unnecessary jumps)

That is a very niche usecase and better served as an optimization instead as part of the language.

  • a way to reduce the jump table entries to smaller (16-bit or even 8-bit) displacements if the cases are close enough together

That is also better served as an optimization, especially as this is very platform specific.

  • a way to jump from one case to another case (determined by a value loaded at runtime) without going back to the top (would help the CPU's branch predictor because there's no longer a single dispatch point)

This can be achieved with a goto.

lainz

  • Hero Member
  • *****
  • Posts: 4725
  • Web, Desktop & Android developer
    • https://lainz.github.io/
Re: how to break out early of a case ?
« Reply #222 on: June 13, 2025, 11:40:31 pm »
I really don't understand what breaking early means...  :'(

Because in Pascal the case breaks automatically when one correct is found.

Do you mean breaking early of a case when you encounter a solution, in the body of the match?

Code: Pascal  [Select][+][-]
  1. case a of
  2. 1: begin
  3.   ...code...
  4.   ...break condition...
  5.   ...code...
  6. end;
  7. 2: begin
  8.   ...code...
  9. end;

A solution can be a nested function, where you have only the case statement. And do 'exit' when needed.

creaothceann

  • Full Member
  • ***
  • Posts: 140
Re: how to break out early of a case ?
« Reply #223 on: June 14, 2025, 12:04:24 am »
  • a way to jump from one case to another case (determined by a value loaded at runtime) without going back to the top (would help the CPU's branch predictor because there's no longer a single dispatch point)

This can be achieved with a goto.

Goto only takes a fixed destination though, hence the "determined by a value loaded at runtime" above. Example:

Code: [Select]
procedure Emulator_6502.Run(var quit : boolean);
label
BRK_continue;
var
Vector : u16;
begin
goto BRK_continue;
while True do begin
case IR of
$00:  // BRK imm  -  BRK (software interrupt) or IRQ/NMI/RESET (hardware interrupt)
begin
if quit then exit;
BRK_continue:
if RESET then Pull(S) else Push(S, PCH      );  Dec(SL);  // cycle 1: push PC
if RESET then Pull(S) else Push(S, PCL      );  Dec(SL);  // cycle 2: push PC
if RESET then Pull(S) else Push(S, Get_P_BRK);  Dec(SL);  // cycle 3: push flags
i := 1;                                                   // set IRQ disable flag
;     if RESET then Vector := $FFFC                       // get vector
else  if NMI   then Vector := $FFFA
else                Vector := $FFFE;
PCL  := Read (Vector     );                               // cycle 4: read PC
PCH  := Read (Vector OR 1);                               // cycle 5: read PC
IR   := Fetch(PC         );  Inc(PC);                     // cycle 6: fetch opcode
Data := Fetch(PC         );  Inc(PC);                     // cycle 7: fetch opcode parameter
goto case IR;  // jump directly to the next opcode handler, not behind the case block
end;
$01:  // ORA (d,X)
begin
//...
Fetch_opcode;
goto case IR;
end;
//...
//...
//...
$FF:  // ISC abs,X  -  undocumented opcode
begin
//...
Fetch_opcode;
goto case IR;
end;
end;
end;
end;
« Last Edit: June 14, 2025, 12:07:07 am by creaothceann »

440bx

  • Hero Member
  • *****
  • Posts: 5569
Re: how to break out early of a case ?
« Reply #224 on: June 14, 2025, 12:36:51 am »
I really don't understand what breaking early means...  :'(

Because in Pascal the case breaks automatically when one correct is found.

Do you mean breaking early of a case when you encounter a solution, in the body of the match?

Code: Pascal  [Select][+][-]
  1. case a of
  2. 1: begin
  3.   ...code...
  4.   ...break condition...
  5.   ...code...
  6. end;
  7. 2: begin
  8.   ...code...
  9. end;

A solution can be a nested function, where you have only the case statement. And do 'exit' when needed.
A nested function is a poor solution because it unnecessarily breaks the code into small pieces.    Unfortunately, that has become a much too common programming habit.

Breaking early of a case is simply getting out of the case when the remaining code does not need to be executed (for whatever reason.)
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

 

TinyPortal © 2005-2018