Making Fatal Hidden Assumptions

Why does it need to?

If we restrict the discussion to hosted environments, the trend seems to be toward 64-bit systems. That provides an address space that should be big enough for at least several decades, even assuming exponential grown in memory sizes. A flat 64-bit virtual address space should be the simplest way to manage this, and the need to run

32-bit code should diminish over time.

Segmentation done right could be useful for bounds checking; assigning a segment to each malloc()ed chunk of memory, and for each declared object, could nearly eliminate buffer overruns. But it hasn't really been done yet, and I'm not convinced it will be in the future.

Why do you think segmentation will come back?

--
Keith Thompson (The_Other_Keith) kst-u@mib.org  
San Diego Supercomputer Center               
We must do something.  This is something.  Therefore, we must do this.
Reply to
Keith Thompson
Loading thread data ...

Something like this *is* done on the AS/400.

When done right, it works quite well (see the AS/400) and allows single-level-store (with "capability" protections). This is a very functional and fast model (and it is "multiprocessor-friendly" and has other good properties).

(Right now, one big penalty for context switches in general is that you lose cached data: TLBs, and RAM-cache in virtual cache systems. This is partly patched-up, in some architectures at least, by tagging TLB entries with "address space identifiers" and doing flushes only when running out of ASIDs, but this is a kludge.)

--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: forget about it   http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Reply to
Chris Torek

Having written a lot of low level stuff in years gone by in assembler, c and c++ I have to agree with you. For *p-- to be invalid when we are looking at possible home brew memory allocations and cleverly aligned objects while allowing an out of range *p++ is a tad inconsistent. Having said that I dont think I ever had any such trap/breakdown so maybe I was lucky or too careful.

Reply to
Richard G. Riley

In article Jordan Abel writes: > On 2006-03-26, Stephen Sprunk wrote: > > It simply doesn't make sense to do things that way since the only > > purpose is to allow violations of the processor's memory protection > > model. Work with the model, not against it. > > Because it's a stupid memory protection model. > > Why can't the trap be caught and ignored?

It can be ignored. But the result is that the operation is a no-op. Again consider: char a[10]; char *p; p = a - 1; p = p + 1; what is the value of p after the fourth statement if the trap in the third statement is ignored?

--
dik t. winter, cwi, kruislaan 413, 1098 sj  amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn  amsterdam, nederland; http://www.cwi.nl/~dik/
Reply to
Dik T. Winter

The trap isn't ignored. There is no trap: the platform's "sane C memory model" compiler and run-time system updated p.array_index to -1 and p.array_base to a.array_base at the third line, as expected. The trap would be left enabled, so that it would actually hit if/when a real pointer was formed from &p.array_base[p.C_pointer_index] if/when *p was ever referenced in the subsequent code.

Consequently, the above code leaves p == a, as expected, and no trap is encountered. Neat, huh?

:-)

--
Andrew
Reply to
Andrew Reilly

On some PDP-11 models (only), the PSW is also addressable as memory, somewhere in the vicinity of 0177660; I don't recall exactly.

Admittedly, even among the CPU design(er)s that do use condition codes I know of no others that provided this option for accessing them.

(I'm not counting cases where an interrupt or trap, and sometimes at least some calls, saves state including the CC on the stack or in memory. That's much more common.)

- David.Thompson1 at worldnet.att.net

Reply to
Dave Thompson

_Whose_ idiom? No programmer I'd respect writes such code intentionally.

Richard

Reply to
Richard Bos

If you are just interested in zero, negative, signed and unsigned overflows, you do not need to read directly these bits. Using conditional branches, you can determine which bits are set. In any sensible architecture the conditional branch instructions do not alter these bits, so by combining conditional branches, multiple bits (such as C and V) can be obtained. The state of negative and zero bits can easily be determined in C-language, however, getting carry, signed overflow half-carry etc. is very problematic.

The PSW and all the general purpose registers (and hence you could get the address of a register :-) are available in the 8 KiB I/O page which is in the top of the physical memory starting at different physical addresses in systems with 16, 18 or 22 physical address bits.

For 18 and 22 address bit systems, the I/O address had to be mapped to the 64 KiB program address space, which on most operating systems required special privileges and consumed 8 KiB of your precious 64 KiB program address space. If you only needed the PSW, direct mapping of the I/O would usually avoided by using the trapping mechanism. I used it mostly to alter the trace bit, but of course, you could get also the N, Z, C, V bits at once.

Paul

Reply to
Paul Keinanen

In article Andrew Reilly writes: > On Mon, 27 Mar 2006 03:07:28 +0000, Dik T. Winter wrote: > > In article Jordan Abel writes: .. > > > Why can't the trap be caught and ignored? > > > > It can be ignored. But the result is that the operation is a no-op. Again > > consider: > > char a[10]; > > char *p; > > p = a - 1; > > p = p + 1; > > what is the value of p after the fourth statement if the trap in the third > > statement is ignored? > > The trap isn't ignored.

Eh? Jordan Abel asked why the trap can not be ignored.

How many instructions will it take in that case to dereference p?

--
dik t. winter, cwi, kruislaan 413, 1098 sj  amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn  amsterdam, nederland; http://www.cwi.nl/~dik/
Reply to
Dik T. Winter

The C standard don't /outlaw/ forming illegal pointer values; they just say that if you do that, they don't say anything more about the behaviour of your code, so if you want defined behaviour, you have to look elsewhere for the definition.

If you're writing code that has, for whatever reason, to rely on non-C-standard definitions, well then, rely on them. I've written code that relies on non-C-standard behaviour, too - but I didn't expect it to port everywhere, and I didn't expect such use to be a requirement on future standardisation to support it, much as I might like to; the leaves-it-undefined /allows/ the code to work where it works.

--
Chris "x.f(y) == f(x, y) == (x, y).f" Dollin
The shortcuts are all full of people using them.
Reply to
Chris Dollin

None or one? I don't know AS/400 assembly language, but it's said that x86 learned from it. That can do scaled indexed access in zero cycles, if the scale factor is one of the usual suspects. A reasonable compiler would hide or elide essentially all of the other operations.

Why does it matter, anyway? AS/400 is no-one's speed demon. The whole show runs on a VM. What the C compiler couldn't hide, the dynamic recompilation engine (JIT) almost certainly could.

It's not as though C is the system's native tongue, nor it's system implementation language. So what else is a system language going to do there?

--
Andrew
Reply to
Andrew Reilly

How much undefined behaviour can you stand? Sure, your code works OK this year, but what if next year's super-optimizer switch takes a different reading on some behaviour that you've coded to, because it was "universally" supported, but never the less undefined. Want to chase down those bugs?

How many substantial applications do you suppose are written, that *only* use defined behaviours? I suspect that the answer is very close to none.

I like C. A lot.

I think that it could do to have a few fewer undefined behaviours, and a few more defined (obvious) behaviours that you could rely on to describe your algorithms.

That's one of the main things that I like about assembly language, btw: it might be all kinds of painful to express an algorithm (although generally not really all that bad), but the instruction descriptions in the data books tell you *precicely* what each one will do, and you can compose your code with no doubts about how it will perform.

[I don't read comp.lang.c, so if you want me to see any replies (hah! :-), you won't take comp.arch.embedded out of the Newsgroups. Of course, I can imagine that just about everyone doesn't care, at this stage...]
--
Andrew
Reply to
Andrew Reilly

No more than what's covered by the defined behaviour on the platforms I'm prepared to support, where `defined` isn't limited to the C standard but over-enthusiatic uses of random other definitions isn't desired.

Were I actively writing C - which at the moment I'm not - I'd have tests to check behaviour, for this reason among others.

That only use behaviour defined by the C standard? Few. That only use behaviour defined by their intended platforms? Rather more.

Well, me too. But that doesn't stop me thinking that the standard seems to be a reasonable compromise between the different requirements, as things stand.

The first half is the reason I'd typically stay away from assembly language, and I'm not convinced about the second unless one goes into the amount of detail I'd happily leave to the compiler-writer.

--
Chris "x.f(y) == f(x, y) == (x, y).f" Dollin
The shortcuts are all full of people using them.
Reply to
Chris Dollin

maybe not that in particular, but *p-- past 0 is no less idiomatic than

*p++ past the end.
Reply to
Jordan Abel

Really? It's not in *my* idiom, because I like to write code that doesn't gratuitously invoke undefined behavior.

--
Ben Pfaff 
email: blp@cs.stanford.edu
web: http://benpfaff.org
Reply to
Ben Pfaff

a circular argument when you are defending the decision to leave it undefined.

Reply to
Jordan Abel

Quite a bit, *provided* that this "undefined" is only in terms of the C standard.

As I have noted elsewhere, doing something like:

#include

invokes undefined behavior. I have no problem with including such a file, though, where the behavior defined by some *other* document is required.

What I try to avoid is:

- depending on behavior that is not only not defined by the C standard, but also not defined by anything else, and merely "happens to work today";

- making use of implementation(s)-specific behavior when there is a well-defined variant of the code that also meets whatever specifications are in use.

The latter covers things like doing arithmetic in "int" that deliberately overflow temporarily, assumes that the overflow does not trap, and then "un-overflows" back into range. If one codes this in "unsigned int" arithmetic instead, one gets guaranteed mod-2-sup-k behavior, and the code is just as small and fast as the not-guaranteed version.

Actually, there are a number of instruction sets (for various machines) that tell you to avoid particular situations with particular instructions. Consider the VAX's "movtuc" ("move translated until character") instruction, which takes a source-and-source-length, destination (and destination-length?), and translation-table. The manual says that the effect of the instruction is unpredictable if the translation table overlaps with the source (and/or destination?).

Someone put a comment into a piece of assembly code in 4.1BSD that read "# comet sucks". I wondered what this was about.

It turns out that whoever implemented the printf engine for the VAX used "movtuc" to find '%' and '\0' characters, and did the movtuc with the source string having "infinite" length (actually

65535 bytes, the length being restricted to 16 bits) so that it often overlapped the translation table. On the VAX-11/780, this "worked right" (as in, did what he wanted it to). On the VAX-11/750 -- known internally as the "Comet" -- it did not behave the way he wanted. The result was that printf() misbehaved for various programs, because the assembly code depended on undefined behavior.

(The "fix" applied, along with the comment, was to limit the length of the source so as not to overlap the table. Of course, when we rewrote the printf engine in C for portability and C89 support, we stopped using movtuc entirely.)

--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: forget about it   http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Reply to
Chris Torek

The C standard, as it exists, makes decrementing a pointer past the beginning of an array undefined behavior. Most of us avoid doing this, not because we think the standard *should* make it undefined, but because the standard *does* make it undefined. Code that does this is not idiomatic, because careful programmers don't write such code. There's nothing circular about that.

Note that you can run into similar problems if you use indices rather than pointers, if the index type is unsigned. The behavior when you decrement past 0 is well-defined, but it's likely to cause problems if the unsigned value is being used as an array index (except that, unlike for pointers, 0-1+1 is guaranteed to be 0).

--
Keith Thompson (The_Other_Keith) kst-u@mib.org  
San Diego Supercomputer Center               
We must do something.  This is something.  Therefore, we must do this.
Reply to
Keith Thompson

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.