News for hackers. Stuff that matters.
Re:Slim Binaries (1997) by jandrese on Wednesday July 31, 2019 @04:14PM (#20577996)
> As I recall they did not do something simple like have monolithic blocks of code separated by architecture: if there was a loop, the body of the loop had interleaved instructions for all architectures. I'm sure this was in the days before instruction caches were a thing.

The first part of your post sounded pretty clever and workable, but this part sounds completely insane. Managing all of the side effects for all of those other architectures constantly would have been an never ending nightmare. Keeping separate blocks and only sharing data segments seems like the only way to keep your sanity.

Re:Slim Binaries (1997) by kazinator on Wednesday July 31, 2019 @05:12PM (#20578597)
Well, there are never going to be side effects for the other architectures, because those parts simply can't be touched. Code for the i386 only jumps to other i386 code. Anything that is not i386 code might as well be donkey droppings. If we think about how sequences of NOP instructions are routinely added to machine code for cache-aligning the start of a block, or how non-code data such a string literals is weaved into the middle of code, it doesn't seem far-fetched at all.
Re:Slim Binaries (1997) by jandrese on Wednesday July 31, 2019 @08:20PM (#20580071)
That makes even less sense. Was the code flow like:

  x86_instruction
  jump +6
  mips instruction
  jump +6
  sparc instruction
  jump +6
  68k instruction
  jump +6
Not separating the code out by blocks seems unworkable to me.
Re:Slim Binaries (1997) by kazinator on Wednesday July 31, 2019 @08:50PM (#20580241)
Sounds like it was like:

     block
     of
     x86
     code
     je L43
     more
     x86
     code
     jmp L57

   L33:
     sparc
     code
     never
     mind

   L43:
     x86
     code
 
   L37:
     sparc
     never
     mind

   L57:
     x86
     code
     again

That kind of thing: blocks of code mashed together, but everything is correctly generated to jumps to its own kind.

In regular compiler-generated machine code, we already have "foreign" blocks of stuff in the middle of the instructions, such as string (and other) literals, and computed branch tables. The generated code doesn't accidentally jump into these things. This is kind of the same: all the other architecture stuff is just a literal (that is not referenced). From the x86 POV, the stuff at L33 and L37 above is just data.

Re:Slim Binaries (1997) by EdwardCoffin on Wednesday July 31, 2019 @09:02PM (#20580301)
No, that's not the way I remember they did it. I think you'd have a run of instructions for one architecture, and the only thing that interrupted a run of them would be a branch or a jump. So for example, if you had something like:

    for (i=0; i<10; i++) {
      // body of loop
    }
Then you'd have all of the x86 instructions for the loop in contiguous bytes, after that block you'd have the MIPS instructions, then the SPARC instructions and so on.

I could be wrong about this, which is why I am now really adamant that I find the article I remember, so I can figure out what they really did. But I think there are three levels of granularity they could have done:

1. They could have had what was essentially a monolithic code block for each architecture, concatenated those blocks and had the little bit of magic at the beginning of the executable figure out which block to dispatch to, then execution would stay in that block. I don't think they did this. 2. At the other extreme, they could interleave individual instructions, like you just described. I don't think they did this either. 3. They could have runs of architecture specific instructions corresponding to basic blocks [1], which is what I tried to describe above. I think that's what they did, but I could easily be misremembering.

[1] https://en.wikipedia.org/wiki/Basic_block

Re:Slim Binaries (1997) by EdwardCoffin on Wednesday July 31, 2019 @06:56PM (#20579463)
As I recall, there were no jumps or branches that there would not have been anyway in a single-architecture binary. Every time there was a jump or branch, it simply had to go further, since it would be skipping over unreachable binary code for the other architectures. The real complexity was all resolved up front, in that blob of magic at the entry point.