Anton Ertl [2023-12-20 21:37:44] wrote:
mitchalsup@aol.com (MitchAlsup) writes:
Anton Ertl wrote:The static scope level is independent of recursion. Note that this
Stephen Fuld <sfuld@alumni.cmu.edu.invalid> writes:How does Pascal do this counting when there are recursive frames on
I believe Pascal, back in the 1970s implemented nested functions, though >>>>> I don't know how any specific implementation accomplished it.AFAIK Pascal implementations all just use static links chains to the
enclosing scopes. So you access an outer local by following the link
chain as many levels as you cross scopes.
the stack ??
kind of implementation has a static link in addition to the dynamic
link.
Maybe I'm missing something but I think Pascal's display is "orthogonal"
to the issue discussed before of how to stuff together a code pointer together with a pointer to its associated data.
Pascal's display is one possible representation of closures, but it
doesn't save you from the need to use a kind of "fat pointer" for the first-class functions.
What saves Pascal is that first-class functions aren't first class at
all, so Pascal can safely and easily represent them any way it likes, including as a pair of two values (a pointer to the code, and a pointer
to a stack frame, typically).
It's harder in C because some code wants to be able to convert
a function pointer to a `void*` and back, so you usually want to make it
fit into the size of a normal pointer.
So what saves C is that nested functions are not part of the language!
And when you have extensions that support nested functions, such as
supported by gcc, if the function really needs a fat pointer you have a trampoline on the stack to hide that and make it look like a normal
"thin" pointer.
On 20/12/2023 23:26, Stefan Monnier wrote:
Anton Ertl [2023-12-20 21:37:44] wrote:
mitchalsup@aol.com (MitchAlsup) writes:
Anton Ertl wrote:The static scope level is independent of recursion. Note that this
Stephen Fuld <sfuld@alumni.cmu.edu.invalid> writes:How does Pascal do this counting when there are recursive frames
I believe Pascal, back in the 1970s implemented nestedAFAIK Pascal implementations all just use static links chains to
functions, though I don't know how any specific implementation
accomplished it.
the enclosing scopes. So you access an outer local by following
the link chain as many levels as you cross scopes.
on the stack ??
kind of implementation has a static link in addition to the dynamic
link.
Maybe I'm missing something but I think Pascal's display is
"orthogonal" to the issue discussed before of how to stuff together
a code pointer together with a pointer to its associated data.
Pascal's display is one possible representation of closures, but it
doesn't save you from the need to use a kind of "fat pointer" for
the first-class functions.
What saves Pascal is that first-class functions aren't first class
at all, so Pascal can safely and easily represent them any way it
likes, including as a pair of two values (a pointer to the code,
and a pointer to a stack frame, typically).
It's harder in C because some code wants to be able to convert
a function pointer to a `void*` and back, so you usually want to
make it fit into the size of a normal pointer.
Converting between function pointers and data pointers (like void*)
is undefined behaviour in C. Of course, some people want to do that regardless, and on many platforms you can probably get away with it
if you are lucky.
On some platforms, however, data pointers and
function pointers are different sizes. (There are even some
platforms where pointers to different types of data are different
sizes.)
But you /are/ allowed to convert back and forth between different
function pointer types in C, which means function pointers are always
the same size. And for efficiency, that would generally be the
smallest size that works for normal functions - i.e., typically just
the address in memory of the function's code.
So what saves C is that nested functions are not part of the
language! And when you have extensions that support nested functions,
such as supported by gcc, if the function really needs a fat pointer
you have a trampoline on the stack to hide that and make it look like
a normal "thin" pointer.
On Wed, 20 Dec 2023 23:51:40 +0100
David Brown <david.brown@hesbynett.no> wrote:
On 20/12/2023 23:26, Stefan Monnier wrote:
Anton Ertl [2023-12-20 21:37:44] wrote:
mitchalsup@aol.com (MitchAlsup) writes:
Anton Ertl wrote:The static scope level is independent of recursion. Note that this
Stephen Fuld <sfuld@alumni.cmu.edu.invalid> writes:How does Pascal do this counting when there are recursive frames
I believe Pascal, back in the 1970s implemented nestedAFAIK Pascal implementations all just use static links chains to
functions, though I don't know how any specific implementation
accomplished it.
the enclosing scopes. So you access an outer local by following
the link chain as many levels as you cross scopes.
on the stack ??
kind of implementation has a static link in addition to the dynamic
link.
Maybe I'm missing something but I think Pascal's display is
"orthogonal" to the issue discussed before of how to stuff together
a code pointer together with a pointer to its associated data.
Pascal's display is one possible representation of closures, but it
doesn't save you from the need to use a kind of "fat pointer" for
the first-class functions.
What saves Pascal is that first-class functions aren't first class
at all, so Pascal can safely and easily represent them any way it
likes, including as a pair of two values (a pointer to the code,
and a pointer to a stack frame, typically).
It's harder in C because some code wants to be able to convert
a function pointer to a `void*` and back, so you usually want to
make it fit into the size of a normal pointer.
Converting between function pointers and data pointers (like void*)
is undefined behaviour in C. Of course, some people want to do that
regardless, and on many platforms you can probably get away with it
if you are lucky.
On both leading general-purpose computing platforms, i.e. on Windows and
on Posix-compatibles, you don't have to be lucky: this type of
conversion is guaranteed to work. Period. In both cases it is an
integral part of dynamic linking APIs.
On some platforms, however, data pointers and
function pointers are different sizes. (There are even some
platforms where pointers to different types of data are different
sizes.)
But you /are/ allowed to convert back and forth between different
function pointer types in C, which means function pointers are always
the same size. And for efficiency, that would generally be the
smallest size that works for normal functions - i.e., typically just
the address in memory of the function's code.
So what saves C is that nested functions are not part of the
language! And when you have extensions that support nested functions,
such as supported by gcc, if the function really needs a fat pointer
you have a trampoline on the stack to hide that and make it look like
a normal "thin" pointer.
David Brown <david.brown@hesbynett.no> schrieb:
So what saves C is that nested functions are not part of the language!
And when you have extensions that support nested functions, such as
supported by gcc, if the function really needs a fat pointer you have a
trampoline on the stack to hide that and make it look like a normal
"thin" pointer.
I looked at what the Fortran compilers I could lay my hands on did
with the following code, which uses a contained subroutine inside
a subroutine:
module test
contains
subroutine t(f)
interface
subroutine f()
end subroutine
end interface
call f()
end subroutine
end module
subroutine foo(i)
use test
integer, intent(inout):: i
call t(bar)
contains
subroutine bar()
call xxx(i)
end
end
Interestingly, every compiler I could lay my hands on on short
notice (gfortran for several architectures, ifort, xlf, the new
flang for llvm) used trampolines.
See https://godbolt.org/z/nv5nr934q , which is for POWER and
contains a call to a subroutine suggestively called
__trampoline_setup.
Thomas Koenig wrote:
David Brown <david.brown@hesbynett.no> schrieb:
So what saves C is that nested functions are not part of the language!
And when you have extensions that support nested functions, such as
supported by gcc, if the function really needs a fat pointer you have a
trampoline on the stack to hide that and make it look like a normal
"thin" pointer.
I looked at what the Fortran compilers I could lay my hands on did
with the following code, which uses a contained subroutine inside
a subroutine:
module test
contains
subroutine t(f)
interface
subroutine f()
end subroutine
end interface
call f()
end subroutine
end module
subroutine foo(i)
use test
integer, intent(inout):: i
call t(bar)
contains
subroutine bar()
call xxx(i)
end
end
Interestingly, every compiler I could lay my hands on on short
notice (gfortran for several architectures, ifort, xlf, the new
flang for llvm) used trampolines.
See https://godbolt.org/z/nv5nr934q , which is for POWER and
contains a call to a subroutine suggestively called
__trampoline_setup.
BTW this requires a RWE stack.
The x64 gfortran is considerably simpler.
It stuffs two MOV reg,imm and a CALL instructions on the stack at RSP+8, copies RSP+8 to RDI, calls test_MOD which jumps to RDI.
bar.0:
mov rdi, QWORD PTR [r10]
jmp xxx_
__test_MOD_t:
jmp rdi
foo_:
sub rsp, 56
mov edx, -17599 // REX MOV reg,imm
mov ecx, -17847 // REX MOV reg,imm
mov QWORD PTR [rsp], rdi
lea rdi, [rsp+8]
mov WORD PTR [rsp+8], dx
mov edx, OFFSET FLAT:bar.0
mov QWORD PTR [rsp+40], 0
mov DWORD PTR [rsp+10], edx
mov WORD PTR [rsp+14], cx
mov QWORD PTR [rsp+16], rsp
mov DWORD PTR [rsp+24], -1864106167 // REX CALL
call __test_MOD_t
add rsp, 56
ret
EricP wrote:
Thomas Koenig wrote:
David Brown <david.brown@hesbynett.no> schrieb:
So what saves C is that nested functions are not part of the
language! And when you have extensions that support nested
functions, such as supported by gcc, if the function really needs a
fat pointer you have a trampoline on the stack to hide that and make
it look like a normal "thin" pointer.
I looked at what the Fortran compilers I could lay my hands on did
with the following code, which uses a contained subroutine inside
a subroutine:
module test
contains
subroutine t(f)
interface
subroutine f()
end subroutine
end interface
call f()
end subroutine
end module
subroutine foo(i)
use test
integer, intent(inout):: i
call t(bar)
contains
subroutine bar()
call xxx(i)
end end
Interestingly, every compiler I could lay my hands on on short
notice (gfortran for several architectures, ifort, xlf, the new
flang for llvm) used trampolines.
See https://godbolt.org/z/nv5nr934q , which is for POWER and
contains a call to a subroutine suggestively called
__trampoline_setup.
BTW this requires a RWE stack.
The x64 gfortran is considerably simpler.
It stuffs two MOV reg,imm and a CALL instructions on the stack at RSP+8,
copies RSP+8 to RDI, calls test_MOD which jumps to RDI.
bar.0:
mov rdi, QWORD PTR [r10]
Where is R10 made to point at i ??
jmp xxx_
__test_MOD_t:
jmp rdi
foo_:
sub rsp, 56
mov edx, -17599 // REX MOV reg,imm
mov ecx, -17847 // REX MOV reg,imm
mov QWORD PTR [rsp], rdi
lea rdi, [rsp+8]
mov WORD PTR [rsp+8], dx
mov edx, OFFSET FLAT:bar.0
mov QWORD PTR [rsp+40], 0
mov DWORD PTR [rsp+10], edx
mov WORD PTR [rsp+14], cx
mov QWORD PTR [rsp+16], rsp
mov DWORD PTR [rsp+24], -1864106167 // REX CALL
call __test_MOD_t
add rsp, 56
ret
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 546 |
Nodes: | 16 (2 / 14) |
Uptime: | 01:13:30 |
Calls: | 10,387 |
Calls today: | 2 |
Files: | 14,061 |
Messages: | 6,416,728 |