Date: Mon, 15 Sep 2025 22:10:02 -0700
On Monday, 15 September 2025 20:53:25 Pacific Daylight Time Nate Eldredge
wrote:
> Sure, but since you can't have an array of functions, comparing function
> pointers falls outside the common case of "comparing pointers within the
> same array" that the < operator is meant for. Here, I agree, you have to
> pay the runtime cost of std::less regardless, and all that your proposal
> changes is the syntax.
I agree, but I come to the opposite conclusion: because operator< is only
defined for items in an array, defining it for functions would imply it needs to
take the full memory space regardless, so on 16-bit DOS it would need to do
32-bit comparisons for __far function pointers, when it doesn't need to do so
for __far data pointers.
> > Besides, std::less<T __far *> can be different from std::less<T __near *>.
>
> Yes, of course they would be different. But that's beside my point.
I don't think so.
[snip]
> In this code model, any two pointers within the same array (or class object)
> must have the same segment part, and the < operator is only specified for
> comparisons of such pointers; so the compiler may *assume* the segments are
> equal, and compare the offsets only. (Likewise in this code model, `p++`
> increments the offset only, and doesn't touch the segment.) So as it
> stands, the test `p < endp` consists of one cheap `cmp` instruction.
I agree.
However, we're talking about functions objects.
> But under your proposal, the compiler would be required to emit code for `p
> < endp` that would have well-specified behavior even if `p` and `endp` do
> not point into the same array. That means it does have to compare the
> segment as well as the offset parts, which at least doubles the runtime
> cost - and much more if we have to handle overlapping segments by computing
> linear addresses (I'm not sure if x86-16 ABIs allowed that).
No, I don't see how that logically follows from what I said. There's no need
for that. I am still agreeing with you that a __far data pointer need only
compare the offset part for ordering.
> In this example, a smart compiler (which typical x86-16 compilers were not)
> could hoist the segment part of the comparison out of the loop, but we
> could certainly construct an example where this would not be possible.
> (And I realize this example has UB if `endp` has a higher address than all
> of `arr`; but greater than all its elements; we could assume the calling
> code avoids that case, or modify this function to handle it.)
>
> All that said, you certainly could make an argument that the benefits of the
> proposal outweigh the costs, but IMHO you'd need data to quantify them.
> The costs do exist.
I don't see how any of this has anything to do with the original proposal,
which was about function objects.
Functions don't exist in arrays, so if they are orderable, the limitation that
their segment portion be the same wouldn't apply. The ordering would need to
be 32-bit, which would be the exact same cost for std::less<void __far *>.
wrote:
> Sure, but since you can't have an array of functions, comparing function
> pointers falls outside the common case of "comparing pointers within the
> same array" that the < operator is meant for. Here, I agree, you have to
> pay the runtime cost of std::less regardless, and all that your proposal
> changes is the syntax.
I agree, but I come to the opposite conclusion: because operator< is only
defined for items in an array, defining it for functions would imply it needs to
take the full memory space regardless, so on 16-bit DOS it would need to do
32-bit comparisons for __far function pointers, when it doesn't need to do so
for __far data pointers.
> > Besides, std::less<T __far *> can be different from std::less<T __near *>.
>
> Yes, of course they would be different. But that's beside my point.
I don't think so.
[snip]
> In this code model, any two pointers within the same array (or class object)
> must have the same segment part, and the < operator is only specified for
> comparisons of such pointers; so the compiler may *assume* the segments are
> equal, and compare the offsets only. (Likewise in this code model, `p++`
> increments the offset only, and doesn't touch the segment.) So as it
> stands, the test `p < endp` consists of one cheap `cmp` instruction.
I agree.
However, we're talking about functions objects.
> But under your proposal, the compiler would be required to emit code for `p
> < endp` that would have well-specified behavior even if `p` and `endp` do
> not point into the same array. That means it does have to compare the
> segment as well as the offset parts, which at least doubles the runtime
> cost - and much more if we have to handle overlapping segments by computing
> linear addresses (I'm not sure if x86-16 ABIs allowed that).
No, I don't see how that logically follows from what I said. There's no need
for that. I am still agreeing with you that a __far data pointer need only
compare the offset part for ordering.
> In this example, a smart compiler (which typical x86-16 compilers were not)
> could hoist the segment part of the comparison out of the loop, but we
> could certainly construct an example where this would not be possible.
> (And I realize this example has UB if `endp` has a higher address than all
> of `arr`; but greater than all its elements; we could assume the calling
> code avoids that case, or modify this function to handle it.)
>
> All that said, you certainly could make an argument that the benefits of the
> proposal outweigh the costs, but IMHO you'd need data to quantify them.
> The costs do exist.
I don't see how any of this has anything to do with the original proposal,
which was about function objects.
Functions don't exist in arrays, so if they are orderable, the limitation that
their segment portion be the same wouldn't apply. The ordering would need to
be 32-bit, which would be the exact same cost for std::less<void __far *>.
-- Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org Principal Engineer - Intel Platform & System Engineering
Received on 2025-09-16 05:10:16