This project is dedicated to the memory of William Morris (aka Frags), who was the main contributor to the bounty but was unable to see the final result.

Showing posts with label planning. Show all posts
Showing posts with label planning. Show all posts

Sunday, July 20, 2014

PPCJITBETA04 (FourFOurfOurfoUR!)


Four is a nice, round number. Power of 2, not too many, not too less. You know, four is referring to many good things, like: the Fantastic Four, 4th of July, AmigaOS4, The Magnificent Four... Err.. Maybe not that, scratch that last one.

Therefore, without further ado, here is Beta #4:


I had an irresistible urge to RickRoll you guys with the link, but that video has been blocked recently on YouTube in many countries, so maybe next time.

Tickets please

I had my sweet time with a very weird bug related to Quake, which is not resolved yet and probably related to the failure to implement the soft cache flushing. These two tickets are pushed back to Beta #5 for now. Previously I had no intention to do one more beta release before the first Release Candidate, but as it seems I need one more round of testing period.

I had to shuffle around some tickets while I was rethinking the upcoming Release Candidate. You know, changing priorities, agile development, whatnot. What is listed in the milestones now is the plan, although it is not set in (mile)stone... Heheh... (Huh, that was a really lame pun. You should do better than that!)

The idea is: I am going to fix every issue which is known and give you guys some time to test before the final release (candidate).

SAM440

After a few rounds of pushing and pulling some SAM440/Flex related codes hopefully we have sorted out all the various problems related to those machines. If you were still experiencing issues then please let me know.

G5 again

Thanks to Tobias Netzel, the flag extraction on G5 is fixed, this will resolve a number of problems with various programs.

The compiling for G5 is still not resolved for MorphOS, no G5-optimized binary again, sorry guys. If you could tell me how can I (easily) compile the files for G5 on my iBook then I give it a go, I promise.

What's next

As you can see: Beta #5 is coming, there are already a handful things lined up for it.

Please do test Beta #4 and please do report bugs you have found. It is important to sort out as many problems as I could. It is equally important to help me reproducing the bug. So, please read the instructions. Thanks a bunch!

Best Boys

I would like to give a big thanks to Luigi Burdo. He helped me with a great deal of things, reported lots of bugs and he is so enthusiastic that he inspired me to keep walking on the road. Thanks a lot, Luigi! Keep up the good spirit!

And, of course, my dearest sidekicks, who just couldn't stay away from the project:
MickJT and Tobias Netzel. Cheers!

Also would like to thank the helps, bug reports and overall support to:
Samir Hawamdeh, Kicko, Chris Handley, Allan Ullmann.

Monday, November 4, 2013

On a collision course

So, where are we at with the recent update? 349 out of 387: no, it is not 100% yet, but a nice, fat 90.1%!

Quickly, what was done this time and let's jump to conclusions right after that:
  • Implementation of BFINS Dx,Da{y,z}, BFFFO Dx{y,z},Da, BFEXTS Dx{y,z},Da, BFEXTU Dx{y,z},Da, BFCLR Dx{y,z}, BFTST Dx{y,z}, BFSET Dx{y,z}, BFCHG Dx{y,z}, ROXR.x Dy,Dz, ROXR.W mem, ROXR.x #imm,Dy, ROXL.x Dy,Dz, ROXL.W mem, ROXL.x #imm,Dy, ASR.W mem, ASL.W mem, LSR.W mem, LSL.W mem, ROR.W mem, ROL.W mem instructions.
  • Opcode compiler fucntions for different operation sizes are unified, unnecessary helper functions were removed.
  • Macroblock protos are generated from the opcode table file rather than used the manually prepared file.
  • Fixed wrong function name for the CNTLZW PowerPC instruction emitter.
  • Fixed missing input register in C and X flag extraction macroblock which is used in some shift instructions.
  • Fixed register dependency in ASR.x Dy,Dz and LSR.x Dy,Dz instructions.
(Slightly unrelated: I have found a bug in the original E-UAE code for BFINS and flags, if I will have enough patience I will fix that. And another one in Petunia, that will be fixed too...)

Now, we are getting dangerously close to the beta stage. It is really hard not to give you guys any promises what I cannot keep later on... (Summer is coming, you know... ;)

Anyway, my plans for the near future (read: when it is done) are:
  1. After I have finished with the implementation of all instructions which were anticipated earlier I am going to stabilize the emulator a bit and clean up the code if needed.
  2. I will try to release a compiled beta version and put together some documentation for using/testing it.
  3. There are some outstanding issues, one of the most critical one is fixing the problems with the macroblock optimizer.
So much for the crystal ball and now...

Something completely different

I have to admit that my posts were not that interesting to read recently. Earlier I invested more effort into the posts and it was probably more fun to read, especially to the developers.
I would like to bring back that tradition, so for this update I came up with a few interesting thoughts on:

How to avoid the decisions

I know that many developers love to procrastinate anything, including (but not limited to) decisions. But what I am about to write is not how lazy my fellow developers are, but rather how to get around a situation when it is not ideal to do a comparison and branch according to the result.

Why would that be important at all?
There are a number of reasons why it is better to avoid branching, this article lists many examples for that and also explains the reasons. It basically boils down to the following reasons:
In our case none of these reasons apply, but unfortunately branching is not really possible due to the static flow analysis on the macroblock list.

To put it simply: each macroblock is depending on the results of the previously executed macroblocks, if we skip ahead then it is impossible to tell whether those required macroblocks were executed or not before the dependent macroblocks.

Why does this cause any trouble? Because sometimes it is pretty hard to avoid conditions inside the instruction implementations.

I already faced this issue earlier, when I started to work on the conditional branching and setting instructions (Bcc and Scc). There was no possible way to avoid the conditional branching for these instructions due to its very nature of the instructions.
I ended up creating a very specific macroblock which embeds the condition checking and branching, so the "inter-macroblock" flow analysis remains intact.

In the recent update you can find many bit field instructions which were complicated enough already and then the very same issue came up again:

For bit field instructions 0 (zero) bit field width means 32 bit actually. This doesn't sound that bad, however sometimes the bit field width is coming from an emulated data register instead of a statically encoded constant. In this case the decision must be done in the emulated code instead of in compiling time.

Not in every case, but quite often it is possible to calculate the result instead of making a comparison and branching.

For the bit field instructions the solution was (written in C code, just because it is easier to understand):

temp = width - 1;
width = width | (temp & 32);

(There is one condition, though: the width must be between 0 (zero) and 31 before this operation starts.)

Now, think about it a little bit and figure out on your own why does it work. I am not going to explain it. :)

See you soon.

Sunday, August 18, 2013

Moving stuff around

Long time no see! No wonder: we just moved to a new house, which usually means lots of things to deal with and boxes wherever I look. (I hate this soo much! I badly need my daily routine, but how can I find my stuff?!) Finally, we are getting settled again and I was able to dig up the good ol' Amiga again under the pile of boxes. And this new house is just awesome, so it worth all the pain we went through.

Interesting coincidence, but right before we started the whole move house craziness (seems like ages ago, BTW) I decided to refactor some code in E-UAE JIT. Namely the handling of the temporary registers.
It was such a bad design, or rather no design at all: I used the number of the allocated temporary registers to index a couple arrays with the relevant data in it. Can you believe this? In 2013? This was unacceptable even in the '70s. There are so many reasons why you shouldn't do that.
Also what is the point of using a strongly typed language, if we don't use distinct types? In this case all the temporary registers along with the actually mapped PowerPC processor registers are passed to the functions as integers. Very easy to misuse. Bad, bad, bad.

In the recent changes I have managed to introduce the concept of a temporary register-specific type structure, which is also able to carry around the mapped PowerPC processor register number and the register dependency map for the macroblocks.

This new structure can be used when the macroblocks are collected (so mostly in the helper functions), but the code emitters for the macroblocks are dealing with the mapped PowerPC registers only. Now, I still need to solve that one: at the moment these are still passed to the functions as integers.

And what is the visible output for you guys? Nothing, if we are lucky. ;)
Yea, I know. It is always hard to justify the time to the business owners what was spent on solving technical debts. But this was a long outstanding one and I always had the itch of fixing it.

So, enjoy your Summer while it lasts and bear with me.

Tuesday, May 1, 2012

Deep-diving into memory

Since I just bought a new SSD and I am about to reinstall my laptop, it is time to do another update on the project: another batch is uploaded to the SourceForge repository. (I hope I won’t lose the sources… ;)

So, what was made into this update? A quick list about what happened in the last two weeks:

  • Direct memory writing support
  • Detecting of special memory accesses per instruction and calling the chipset emulation on memory writes from the compiled code
  • Implementation of some instructions:
    • ADDQ.L;
    • MOVE.x register to memory;
    • MOVE.x register to register;
  • Implementation of some addressing modes:
    • immediate quick, like: ADDQ.L #x,reg;
    • indirect addressing with post increment, like: MOVE.x reg,(Ay)+;
  • Misc fixed stuff:
    • proper logging for JIT (with a new configuration item: comp_log);
    • slightly reworked temporary register handling;
    • better handling of reloading/saving of flags and the base register;
    • reloading of the emulated PC before leaving the compiled code is added (probably this was responsible for the reboot-loop at booting the OS, see below).

The most important change was the handling of the memory writes. When I started to plan the project this was one of the concerns: how to figure out which memory access requires special handling and which one can hit directly the emulated memory. By overcome of this issue all three problems are resolved:

  • memory accesses are emulated as it is needed;
  • self-modifying code is handled by using the processor cache;
  • translated code lookup is managed by simulating the cache lines.
Luckily, the x86 JIT implementation already solved this issue (too): every instruction is executed by the interpretive emulator first for a couple times to prevent unnecessary translation of the code pieces that won’t be executed often (like initialization/cleanup code). While it is doing that it collects some information about the executed code that can be reused for the translation. One of these is the memory access type. Very clever solution, I must admit.

I am still chasing the ghost-bug that prevents the OS from booting. The recent fixes for the PC register (program counter) reloading changed the behaviour. Instead of running around in a reboot-loop, the OS is waiting for something to happen, most likely some hardware to trigger something. So, it is still no good, but looks a bit better.

Where to go from here: more addressing modes and instructions will be implemented before I start working on the implementation of the optimizing routines. First, I want to make sure that most of the Mandelbrot test is executed using compiled code, thus it will be easier to test the optimization and finally we can see how better the JIT performs than the interpretive. Exciting!

You have probably noticed that the state of the project on SourceForge is still pre-alpha. I don’t want to change it while the OS cannot be run on the emulation, so this is the other important goal.

So much for today. While you are enjoying the sunny, warm summer, don’t forget about us who are suffering from the cold, rainy days: Winter is coming… :/

Sunday, March 27, 2011

Rainy Day with Copper-rendered Rainbows

The sky is cloudy, sometimes sudden showers are soaking the insanely green grass and I am forced into the shabby unit we rented half a year ago. Time to move - popping into my mind quite often these days, but that is a completely different matter.
There are only a few things distracting me from working on E-UAE, I tried to concentrate working on it with a great strength. (Apage Satanas: Facebook! I clearly miss the times when I was developing Flamingo. I was sitting in my curtained flat all day, no internet, no phone, no tv: the best environment for creating your own little pet creature and take over the world, MUHAHAH...)

I was trying to compile E-UAE since my last post, but I am clearly not really fond of the multi-platform environment. Configure script is a bad dog: never behaves. (Linux developers must have an insane amount of time and patience.)
Finally, after running into the never-ending circle of deleting all, copy back, adjusting, configuring and do the make for the hundredth time: I was able to compile E-UAE sources, and the binary is even working. YAY! \o/

I am happy that I can start on tinkering with the JIT implementation, but I have to admit that my Amiga developer skills became a bit rusty in the last three years, since I hardly had time to spend on development.
I fought big time with the path of include files, still getting flooded with complaints about missing prototypes for the AOS functions, but at least that is only a warning. (Not that I can live with compile-time warnings for my code, but I really don't have enough strength to find what minor thing needs fine tuning in the compiling to get rid of these warnings safely.)
It took me an hour today to find out that TimeDelay function is part of the (obsolete) libamiga and this is why the linking failed every time. There was an easy fix for that: just remove the check for AOS4 from the sources and let it fall back to the DOS/Delay() function. Not a proper solution, because Delay() is badly inaccurate, but at least I was able to compile and fire it up.

The compiling on the uA1 is awesomely slow and eating up all the memory, so I had to turn on the swapping. I seriously considering the cross-compiling from my windows laptop, and let the A1 do the testing functionality only. It was a very long time since I tried cross-compiling for the OS kernel, back in the days when it was impossible to compile it on AOS4. It was painful, I had to use Cygwin and a number of tricks to get it working. Then copied the compiled binary to my Amiga 4000 on a floppy disk for testing... *Sigh* I was soo patient, I am amazed now. ;)
Speaking of which: I still need net connection for the uA1, that would be essential for the cross-compiling.

Anyway, let's go back to experimenting. The first test will be implementing a very-very simple form of the poor-man's JIT, which will do nothing more than "compiling" the executed code into series of calls to the interpretive instruction implementation functions. The outcome will be an environment, where I still have all the already implemented instructions, but I can replace them one-by-one with the real JIT instructions. Something what I needed when developed Petunia, but there was no interpretive emulator to back up the missing instructions.

Sunday, March 6, 2011

Blueprints

It is time to celebrate: finally my Amiga configuration is complete. It took more than a half year after moving to the opposite side of the globe (from Hungary to New Zealand). I dragged my poor, old A1-XE with me in a suitcase; unfortunately it didn't survive the flight. :(
Stephen “Cobra” Fellner lent a uA1 to me in a compact case, I couldn't be grateful enough for it. (Not to mention the tons of helps they gave us to start our new life here from scratch...)
Piece-by-piece the machine was completed; the final item was an old Philips monitor from TradeMe.

It is also time to reorganize priorities in my (rather limited) free time. Cut back on beaches, bushwalks and especially on Facebook. :P
But enough from whining, this is a technical blog after all and the autumn is coming rapidly with lotsa rain...

Let's scheme!

A JIT compiler similar to a programming language compiler, the main difference is the source: while a programming language source is human readable text (or that would be the goal, at least), the source in this case is machine code from a different processor.
The upside is the machine code has very strict rules, easier to interpret. The downside is it must be precisely known in every minor detail and there are undocumented features and side-effects that have to be implemented correctly. (How to find out these: good question; usually with countless hours of debugging crashing applications.)

Why JIT called just-in-time compiling: the program code translated to natively executable code while it is emulated. It means every time the execution reaches a point in the program that wasn’t reached before then the compiler takes a chunk from the emulated code and translates it to native code then the execution flow goes on. (Some JIT compilers are able to translate the whole executable right after loading, but that is only possible in special cases.)
The compilation process can be either fairly simple or overly complicated, depending on the actual method. The final result must be directly executable on the host system; in other case it would be inefficient and probably useless. (It would require an interpreter to execute, rarely makes any sense.)
There are different approaches to the compilation process, which one is the best choice depends on multiple factors.

Poor man’s JIT

For example the most simplistic method produces a series of jump instructions for execution of source data fetching, operation execution, result store. Everything else is done by a library of functions in the emulation environment. This solution gets rid of the interpretation of every instruction code at every (re-)execution, but it is next to impossible to do any optimization which would involve the source- and destination handling together with the operation; not to mention a wider span of instructions.
Why anybody would try such an inefficient solution: if the interpreter is already implemented then requires significantly less work to turn it into a JIT compiler by this way. Also the translated code needs (lots of) memory, such implementation is lightweight on the memory usage.

Stamping Lil’ Roses and Rainbows

Slightly better approach is creating templates for certain addressing modes and operations that can be copied into the translated code (almost) directly. Not every instruction incarnation needs its own template; it is possible filling up some gaps regarding the specifics to the actual instruction, such as the involved registers.
This is the most common approach, flexible and efficient, if implemented properly. With some tweaking it is even possible to adjust the templates to handle special cases, such as optimizing arithmetic flag emulation away when consecutive instructions would overwrite it anyway. This is how Petunia works and as I found out recently, something similar that WinUAE implements.
However, templates are a bit rigid sometimes and still not to easy (or even impossible) to join together more, than two instructions in a specific optimization, which would make use of certain aspect of the target processor. Creating truly flexible templates could result a big mess in translation functions.

Big Planz I haz it

I had a couple years after finishing (the never-ever finished) Petunia playing around with scenarios in my mind where emulated code builds up this or that way. Finally, I came to the conclusion that there is a possible better way to implement JIT compiling, and that is similar to the microcode, that used often in CISC processors.
Microcode is for reducing the complexity of the machine code instruction by implementing it as a series of very simple “wired” instructions and an “interpreter” executes the simple instructions one-by-one at each clock cycle.
Combining this technique with the VLIW approach, when the simple instructions are executed out-of-order, or even can be eliminated completely the result might be lot more optimal on the generated code.
How do I intend to implement… Similarly to the templates, each emulated instruction will be prepared as a series of virtual instructions. The compiler in the first round collects the virtual instructions for each emulated instructions into a buffer for a defined chunk of the emulated code.
In the second round an optimizer runs trough the buffer while trying to apply modifications on the virtual instructions according to predefined rules. At this level the virtual instructions and the original (emulated) instructions have no connection at all anymore, each virtual instruction can be handled, reorganized, eliminated on its own.
The third round is the code emitter: turns the virtual instructions into natively executable code using actual code templates.

I am sure it wasn’t me who thought on this solution for the first time, but never read about similar approach before in the case of the JIT compiling. Programming language compilers do similar code translation to maintain the portability of the compiler between the different processor architectures. (Code emitter has to be adapted, the rest of the compiler needs no modification.)

Predicted Roadblocks

Once I heard: if you were not able to summarize the problem then you won't be able to find the solution either. Let's find out the possible problems with JIT compiling for the complete machine emulation then.

1. Memory access emulation
Unless the 68k emulation in OS4 the UAE is a complete machine emulation. While an application is running it reads and writes memory (no news to anybody, I guess). If the accessed memory is plain data then there is not much to do with it: the application can do whatever it was planned for.
Unfortunately there are two types of memory access that needs special care: accessing hardware registers and writing into the executed code area. For the latter see below at self-modifying code, the former is lot more easily to handle.
Solution: basically what needed is incorporate the functions from UAE that are called for each memory accesses into the translated code.

2. Self-modifying code
The 68k processors don't make difference between data and code, although the AmigaOS itself is able to recognize what part of the loaded executable contains actual code. The difference was never enforced to the developers with all of its up- and downsides, they found it out by the hard way what can of worms is hidden there, when the processors with cache (like 68020) appeared in Amigas. Several self-modifying game and demo fails running on the cached code memory, because the code cache and the data cache is separated.
In AmigaOS4 I was able to get information from DOS.library regarding the loaded and removed code segments. By using this information I could tell which memory areas are cleared, I simply dropped the translated code for those.
With UAE the situation is completely different: any byte in the memory is potential target for modifying. It means the translated code must be dropped and retranslated; otherwise it would conserve the previously translated state.
Solution: probably I must extend the memory access checking and drop the translated code when it gets modified. I have to revisit this topic later on; checking for translated code at every memory access might be too slow.

3. Translated code lookup
When the execution jumps, branches to a new memory address the emulator has to know whether there is an already translated code or the new address was never hit before, new translation is needed. The translated parts are sometimes following each other, but often the programs are wondering around in the memory with no logic to follow.
Solution: Petunia had the same problem, I created a two level look-up table for the translated code segments for each address in the address space; a bit memory-hungry, but very quick for finding the address of the translated code.

As Michael Jackson would say: This is it. These are the initial plans, more details must follow, but first thing first: let’s try to compile E-UAE… :)