elena-lang
elena-lang copied to clipboard
PowerPC support
Since Linux support already exists, we might check the possibility of adding PowerPC support in JIT.
The big problem is in the existing assembly code, it would take a lot of effort to port the GC and other parts written in Asssembly to PPC Assembly Recalling an issue (which I could not find), of porting all assembly code to C, would make it easier to add to new architectures
Yes, this would be an option. I do plan to move the assembly code to C, but not now. First Linux version should be migrated to ELENA 3.4 first.
I already have an IBM PowerPC computer, with OpenSuse installed, ready to start the implementation for PPC64 :)
😄
My current plan consists of two parts : first I move functions to prepare the program start. Then in October I will migrate GC, but part of the code will remain written in assembler : like command set, core api.
I would like to know if there has been any progress on implementation for ppc, ppc64 or ppc64el ?
Hi, currently it is on hold. The person who would like to do it initially is no longer interested in the project. If there will be someone who could help me with it, I will reopen the issue.
Actually, I'm still interested my great friend @arakov!!! :) @neosaldina I just started the backend development for PowerPC, but as @arakov said he would migrate much of the Assembly code to C, I decided to wait :)
@neosaldina To start development for PowerPC, I will initially have to know how support for Linux is ... I currently know it is stopped.
The steps I will follow to implement ppc support (32bits only initially)
- Analyze everything that will need to be modified in source code to support big endian
- Assembler Implementation
- GC and basic e-code conversion to ppc
- Linker modification to generate ELF for ppc
- Development of JIT for ppc
are quite big modifications ... but over time will be developed As said by Mr. @arakov on issue #493 , he intends to finish refactoring development by the end of January, at exactly the time I will be moving house, city and state ... so the start will be slow... I have a prototype started, for ppc and s390 ... but, it's just prototype ....
Just saving a document that IBM has already removed from their site containing the ppc32 documentation
Thanks, I will add support of ppc32 to asm2binx. After it we could look into 64 bit versions as well (after amd64 support of course)
Ok! The things I added in asm2binx are pretty simple ... basically all registers and the implementation of a method to encode opcode...
enum OperandType
{
otUnknown = 0,
otNone = 0,
otDD = 0x00100005,
otDB = 0x00200005,
otDW = 0x00400005,
otDQ = 0x02000005,
// ******************************
// *** General Purpose Registers
// ******************************
otR0 = 0x00000000,
otR1 = 0x00000001,
otR2 = 0x00000002,
otR3 = 0x00000003,
otR4 = 0x00000004,
otR5 = 0x00000005,
otR6 = 0x00000006,
otR7 = 0x00000007,
otR8 = 0x00000008,
otR9 = 0x00000009,
otR10 = 0x0000000A,
otR11 = 0x0000000B,
otR12 = 0x0000000C,
otR13 = 0x0000000D,
otR14 = 0x0000000E,
otR15 = 0x0000000F,
otR16 = 0x00000010,
otR17 = 0x00000011,
otR18 = 0x00000012,
otR19 = 0x00000013,
otR20 = 0x00000014,
otR21 = 0x00000015,
otR22 = 0x00000016,
otR23 = 0x00000017,
otR24 = 0x00000018,
otR25 = 0x00000019,
otR26 = 0x0000001A,
otR27 = 0x0000001B,
otR28 = 0x0000001C,
otR29 = 0x0000001D,
otR30 = 0x0000001E,
otR31 = 0x0000001F,
// ****************************
// *** Float point registers
// ****************************
otF0 = 0x00000000,
otF1 = 0x00000001,
otF2 = 0x00000002,
otF3 = 0x00000003,
otF4 = 0x00000004,
otF5 = 0x00000005,
otF6 = 0x00000006,
otF7 = 0x00000007,
otF8 = 0x00000008,
otF9 = 0x00000009,
otF10 = 0x0000000A,
otF11 = 0x0000000B,
otF12 = 0x0000000C,
otF13 = 0x0000000D,
otF14 = 0x0000000E,
otF15 = 0x0000000F,
otF16 = 0x00000010,
otF17 = 0x00000011,
otF18 = 0x00000012,
otF19 = 0x00000013,
otF20 = 0x00000014,
otF21 = 0x00000015,
otF22 = 0x00000016,
otF23 = 0x00000017,
otF24 = 0x00000018,
otF25 = 0x00000019,
otF26 = 0x0000001A,
otF27 = 0x0000001B,
otF28 = 0x0000001C,
otF29 = 0x0000001D,
otF30 = 0x0000001E,
otF31 = 0x0000001F,
};
PPCAssembler::Operand PPCAssembler::defineRegister(TokenInfo& token)
{
// ******************************
// *** General Purpose Registers
// ******************************
if (token.check("r0")) {
return PPCHelper::otR0;
}
else if (token.check("r1")) {
return PPCHelper::otR1;
}
else if (token.check("r2")) {
return PPCHelper::otR2;
}
else if (token.check("r3")) {
return PPCHelper::otR3;
}
else if (token.check("r4")) {
return PPCHelper::otR4;
}
else if (token.check("r5")) {
return PPCHelper::otR5;
}
else if (token.check("r6")) {
return PPCHelper::otR6;
}
else if (token.check("r7")) {
return PPCHelper::otR7;
}
else if (token.check("r8")) {
return PPCHelper::otR8;
}
else if (token.check("r9")) {
return PPCHelper::otR9;
}
else if (token.check("r10")) {
return PPCHelper::otR10;
}
else if (token.check("r11")) {
return PPCHelper::otR11;
}
else if (token.check("r12")) {
return PPCHelper::otR12;
}
else if (token.check("r13")) {
return PPCHelper::otR13;
}
else if (token.check("r14")) {
return PPCHelper::otR14;
}
else if (token.check("r15")) {
return PPCHelper::otR15;
}
else if (token.check("r16")) {
return PPCHelper::otR16;
}
else if (token.check("r17")) {
return PPCHelper::otR17;
}
else if (token.check("r18")) {
return PPCHelper::otR18;
}
else if (token.check("r19")) {
return PPCHelper::otR19;
}
else if (token.check("r20")) {
return PPCHelper::otR20;
}
else if (token.check("r21")) {
return PPCHelper::otR21;
}
else if (token.check("r22")) {
return PPCHelper::otR22;
}
else if (token.check("r23")) {
return PPCHelper::otR23;
}
else if (token.check("r24")) {
return PPCHelper::otR24;
}
else if (token.check("r25")) {
return PPCHelper::otR25;
}
else if (token.check("r26")) {
return PPCHelper::otR26;
}
else if (token.check("r27")) {
return PPCHelper::otR27;
}
else if (token.check("r28")) {
return PPCHelper::otR28;
}
else if (token.check("r29")) {
return PPCHelper::otR29;
}
else if (token.check("r30")) {
return PPCHelper::otR30;
}
else if (token.check("r31")) {
return PPCHelper::otR31;
}
// ****************************
// *** Float point registers
// ****************************
else if (token.check("f0")) {
return PPCHelper::otF0;
}
else if (token.check("f1")) {
return PPCHelper::otF1;
}
else if (token.check("f2")) {
return PPCHelper::otF2;
}
else if (token.check("f3")) {
return PPCHelper::otF3;
}
else if (token.check("f4")) {
return PPCHelper::otF4;
}
else if (token.check("f5")) {
return PPCHelper::otF5;
}
else if (token.check("f6")) {
return PPCHelper::otF6;
}
else if (token.check("f7")) {
return PPCHelper::otF7;
}
else if (token.check("f8")) {
return PPCHelper::otF8;
}
else if (token.check("f9")) {
return PPCHelper::otF9;
}
else if (token.check("f10")) {
return PPCHelper::otF10;
}
else if (token.check("f11")) {
return PPCHelper::otF11;
}
else if (token.check("f12")) {
return PPCHelper::otF12;
}
else if (token.check("f13")) {
return PPCHelper::otF13;
}
else if (token.check("f14")) {
return PPCHelper::otF14;
}
else if (token.check("f15")) {
return PPCHelper::otF15;
}
else if (token.check("f16")) {
return PPCHelper::otF16;
}
else if (token.check("f17")) {
return PPCHelper::otF17;
}
else if (token.check("f18")) {
return PPCHelper::otF18;
}
else if (token.check("f19")) {
return PPCHelper::otF19;
}
else if (token.check("f20")) {
return PPCHelper::otF20;
}
else if (token.check("f21")) {
return PPCHelper::otF21;
}
else if (token.check("f22")) {
return PPCHelper::otF22;
}
else if (token.check("f23")) {
return PPCHelper::otF23;
}
else if (token.check("f24")) {
return PPCHelper::otF24;
}
else if (token.check("f25")) {
return PPCHelper::otF25;
}
else if (token.check("f26")) {
return PPCHelper::otF26;
}
else if (token.check("f27")) {
return PPCHelper::otF27;
}
else if (token.check("f28")) {
return PPCHelper::otF28;
}
else if (token.check("f29")) {
return PPCHelper::otF29;
}
else if (token.check("f30")) {
return PPCHelper::otF30;
}
else if (token.check("f31")) {
return PPCHelper::otF31;
}
else return Operand(PPCHelper::otUnknown);
}
The big secret is how to encode opcode, which is not at all complex....
static void writeOpSegment(MemoryWriter* code, int opCode, int size, int position)
{
unsigned int tmpOpCode = opCode;
int mask = (1 << size) - 1;
tmpOpCode &= mask;
tmpOpCode <<= (wordsize - 1 - position);
code->writeDWord(tmpOpCode);
}
Here, a small sample program:
#include <stdio.h>
#define WORD_SIZE 32
typedef unsigned int u32;
u32 OpHex = 0;
void WriteIntToFile(const char* desc, int val)
{
printf("%s = %08X\n", desc, val);
}
int GetShiftNum(int endPos)
{
return (WORD_SIZE - 1 - endPos);
}
int GetOpSegment(int val, int size, int pos)
{
int mask = (1 << size) - 1;
val &= mask;
val <<= GetShiftNum(pos);
return val;
}
void STW(const char* desc, int DestReg, int AddressReg, int Immediate)
{
// STW opcode = 0x24
OpHex = GetOpSegment(0x24, 6, 5);
OpHex |= GetOpSegment(DestReg, 5, 10);
OpHex |= GetOpSegment(AddressReg, 5, 15);
OpHex |= GetOpSegment(Immediate, 16, 31);
WriteIntToFile(desc, OpHex);
}
void STWU(const char* desc, int DestReg, int AddressReg, int Immediate)
{
// STW opcode = 0x25
OpHex = GetOpSegment(0x25, 6, 5);
OpHex |= GetOpSegment(DestReg, 5, 10);
OpHex |= GetOpSegment(AddressReg, 5, 15);
OpHex |= GetOpSegment(Immediate, 16, 31);
WriteIntToFile(desc, OpHex);
}
void OR(const char* desc, int DestReg, int SourceReg1, int SourceReg2)
{
// OR opcode = 0x1F
OpHex = GetOpSegment(0x1F, 6, 5);
OpHex |= GetOpSegment(SourceReg1, 5, 10);
OpHex |= GetOpSegment(DestReg, 5, 15);
OpHex |= GetOpSegment(SourceReg2, 5, 20);
OpHex |= GetOpSegment(444, 10, 30);
WriteIntToFile(desc, OpHex);
}
int main()
{
STWU("stwu\tr1, -0x20(r1)", 0x00000001, 0x00000001, -0x20);
STW ("stw \tr31, +0x4(r1)", 0x0000001F, 0x00000001, 28);
OR("or \tr31,r1,r1", 31, 1, 1);
STW ("stw \tr31, +0x18(r31)", 0x00000003, 0x0000001F, 8);
STW ("stw \tr31, +0x14(r31)", 0x00000004, 0x0000001F, 12);
STW ("stw \tr31, +0x10(r31)", 0x00000005, 0x0000001F, 16);
STW ("stw \tr31, +0xC0(r31)", 0x00000006, 0x0000001F, 20);
return 0;
}
One thing I don't know how complex it will be to implement is the PowerPC branch system... Branch instructions in PowerPC use the distance from the offset of the branch instruction to the next instruction to be executed, look at picture... there is two instructions with the same opcode encoding... the last byte "0x14" is the distance of offset 0x00010898 to the next instruction to be executed, on offset 0x000108AC ... this value could be positive or negative...
Yes, it is similar to x86.
I was seeing where to add in TODO.TXT, about this implementation, but, I don't know what is the correct tag to use, ydev, xdev and etc ...
it doesn't matter, you may use xdev for example
So what operation system should be supported? Probably Linux?
Yeap, for ppc32 and ppc64, only Linux, a old version of Ubuntu and Debian Ports
#77 issue : GC routine will be moved into C code, so the most complex part of CPU-dependent code will be removed. Remaining issue is floating-point opcodes in coreapi and core
great!! That was the part that was scaring me the most ... I understood only part of the code! The floating point is not such a big problem ...
👍
The Power8 ISA is the Power ISA v.2.07 : https://en.wikipedia.org/wiki/Power_ISA#Power_ISA_v.2.07
The P8 are LittleEndian
Some good resource to learn: https://godbolt.org/z/Mf9MM6
The simplest program is now supported for ppc64le
done