From: ThatWouldBeTelling@thevillage.com   
      
   Niklas Holsti wrote:   
   > On 2025-11-06 20:28, MitchAlsup wrote:   
   >>   
   >> Niklas Holsti posted:   
   >>   
   >>> On 2025-11-05 23:28, MitchAlsup wrote:   
   >>>>   
   >>>> Niklas Holsti posted:   
   >>> ----------------   
   >>>>> But then you could get the problem of a longjmp to a setjmp value that   
   >>>>> is stale because the targeted function invocation (stack frame) is no   
   >>>>> longer there.   
   >>>>   
   >>>> But YOU had to pass the jumpbuf out of the setjump() scope.   
   >>>>   
   >>>> Now, YOU complain there is a hole in your own foot with a smoking gun   
   >>>> in your own hand.   
   >>>   
   >>> That is not the issue. The question is if the semantics of "goto   
   >>> label-valued-variable" are hard to define, as Ritchie said, or not, as   
   >>> Anton thinks Stallman said or would have said.   
   >>   
   >> So, label-variables are hard to define, but function-variables are not   
   >> ?!?   
   >   
   > Depends on the level at which you want to define it.   
   >   
   > At the machine level, where semantics are (usually) defined for each   
   > instruction separately, a jump to a dynamic address (using a   
   > "label-variable") is not much different from a call to a dynamic address   
   > (using a "function-variable"), and the effect of the single instruction   
   > on the machine state is much the same as for the static address case.   
   > The higher-level effect on the further execution of the program is out   
   > of scope, whatever the actual value of the target address in the   
   > instruction.   
   >   
   > It is only if your machine has some semantics for instruction   
   > combinations, such as your VEC-LOOP pair, that you have to define what   
   > happens if a jump or call to some address leads to later executing only   
   > some of those instructions or executing them in the wrong order, such as   
   > trying to execute a LOOP without having executed a preceding VEC.   
   >   
   > At the higher programming-language level, the label case can be much   
   > harder to define and less useful than the function case, depending on   
   > the programming language and its abstract model of execution, and also   
   > depending on what compile-time checks you assume.   
   >   
   > Consider an imperative language such as C with no functions nested   
   > within other functions or other blocks (where by "block" I mean some   
   > syntactical construct that sets up its local context with local   
   > variables etc.). If you have a function-variable (that is, a pointer to   
   > a function) that actually refers to a function with the same parameter   
   > profile, it is easy to define the semantics of a call via this function   
   > variable: it is the same as for a call that names the referenced   
   > function statically, and such a call is always legal. Problems arise   
   > only if the function-variable has some invalid value such as NULL, or   
   > the address of a function with a different profile, or some code address   
   > that does not refer to (the start of) a function. Such invalid values   
   > can be prevented at compile time, except (usually) for NULL.   
   >   
   > In the same language setting, the semantics of a jump using a   
   > label-variable are easy to define only if the label-variable refers to a   
   > label in the same block as the jump. A jump from one block into another   
   > would mess up the context, omitting the set-up of the target block's   
   > context and/or omitting the tear-down of the source block's context. The   
   > further results of program execution are machine-dependent and so   
   > undefined behavior.   
   >   
   > A compiler could enforce the label-in-same-block rule, but it seems that   
   > GNU C does not do so.   
   >   
   > In a programming language that allows nested functions the same kind of   
   > context-crossing problems arise for function-variables. Traditional   
   > languages solve them by allowing, at compile-time, calls via   
   > function-variables only if it is certain that the containing context of   
   > the callee still exists (if the callee is nested), or by (expensively)   
   > preserving that context as a dynamically constructed closure. In either   
   > case, the caller's context never needs to be torn down to execute the   
   > call, differing from the jump case.   
   >   
   > In summary, jumps via label-variables are useful only for control   
   > transfers within one function, and do not help to build up a computation   
   > by combining several functions -- the main method of program design at   
   > present. In contrast, calls via function-variables are a useful   
   > extension to static calls, actually helping to combine several functions   
   > in a computation, as shown by the general adoption of   
   > class/object/method coding styles.   
   >   
   > Niklas   
   >   
      
   I was curious about the interaction between dynamic stack allocations   
   and goto variables to see if it handled the block scoping correctly.   
   Ada should have the same issues as C.   
   It appears GCC x86-64 15.2 with -O3 does not properly recover   
   stack space with dynamic goto's.   
      
   Test1 allocates a dynamic sized buffer and has a static goto Loop   
   for which GCC generates a jne .L6 to a mov rsp, rbx that recovers   
   the stack allocation inside the {} block.   
      
   Test2 is the same but does a goto *dest and GCC does not generate   
   code to recover the inner {} block allocation. It just loops over   
   the sub rsp, rbx so the stack space just grows.   
      
   long Sub (long len, char buf[]);   
      
   void Test1 (long len)   
   {   
    long ok;   
      
    Loop:   
    {   
    char buf[len];   
      
    ok = Sub (len, buf);   
    if (ok)   
    goto Loop;   
    }   
   }   
      
   # Compilation provided by Compiler Explorer at https://godbolt.org/   
   Test1(long):   
    push rbp   
    mov rbp, rsp   
    push r13   
    mov r13, rdi   
    push r12   
    lea r12, [rdi+15]   
    push rbx   
    shr r12, 4   
    sal r12, 4   
    sub rsp, 8   
    jmp .L2   
   .L6:   
    mov rsp, rbx   
   .L2:   
    mov rbx, rsp   
    sub rsp, r12   
    mov rdi, r13   
    mov rsi, rsp   
    call Sub(long, char*)   
    test rax, rax   
    jne .L6   
    lea rsp, [rbp-24]   
    pop rbx   
    pop r12   
    pop r13   
    pop rbp   
    ret   
      
   void Test2 (long len)   
   {   
    long ok;   
    void *dest;   
      
    dest = &&Loop;   
    Loop:   
    {   
    char buf[len];   
      
    ok = Sub (len, buf);   
    if (ok)   
    goto *dest;   
    }   
   }   
      
   Test2(long):   
    push rbp   
    mov rbp, rsp   
    push r12   
    mov r12, rdi   
    push rbx   
    lea rbx, [rdi+15]   
    shr rbx, 4   
    sal rbx, 4   
   .L8:   
    sub rsp, rbx   
    mov rdi, r12   
    mov rsi, rsp   
    call Sub(long, char*)   
    test rax, rax   
    jne .L8   
    lea rsp, [rbp-16]   
    pop rbx   
    pop r12   
    pop rbp   
    ret   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|