(* BSD 2-Clause License Copyright (c) 2018, 2019, Anton Krotov All rights reserved. *) MODULE AMD64; IMPORT CODE, BIN, WR := WRITER, CHL := CHUNKLISTS, MACHINE, LISTS, PATHS, REG, C := CONSOLE, UTILS, mConst := CONSTANTS, S := STRINGS, PE32, ELF, X86; CONST rax = REG.R0; r10 = REG.R10; r11 = REG.R11; rcx = REG.R1; rdx = REG.R2; r8 = REG.R8; r9 = REG.R9; rsp = 4; rbp = 5; rsi = 6; rdi = 7; je = 84H; jne = 85H; jl = 8CH; jge = 8DH; jle = 8EH; jg = 8FH; jb = 82H; sete = 94H; setne = 95H; setl = 9CH; setge = 9DH; setle = 9EH; setg = 9FH; setc = 92H; setnc = 93H; shl = CODE.opLSL2; shr = CODE.opLSR2; sar = CODE.opASR2; ror = CODE.opROR2; sCODE = BIN.PICCODE; sDATA = BIN.PICDATA; sBSS = BIN.PICBSS; sIMP = BIN.PICIMP; TYPE COMMAND = CODE.COMMAND; Number = POINTER TO RECORD (LISTS.ITEM) value: INTEGER END; OPRR = PROCEDURE (reg1, reg2: INTEGER); VAR R: REG.REGS; Numbers: LISTS.LIST; Numbers_Count: INTEGER; Numbers_Offs: INTEGER; prog: BIN.PROGRAM; dllret: INTEGER; Win64RegPar: ARRAY 4 OF INTEGER; SystemVRegPar: ARRAY 6 OF INTEGER; PROCEDURE OutByte (b: BYTE); BEGIN X86.OutByte(b) END OutByte; PROCEDURE OutByte2 (a, b: BYTE); BEGIN OutByte(a); OutByte(b) END OutByte2; PROCEDURE OutByte3 (a, b, c: BYTE); BEGIN OutByte(a); OutByte(b); OutByte(c) END OutByte3; PROCEDURE OutInt (n: INTEGER); BEGIN OutByte(MACHINE.Byte(n, 0)); OutByte(MACHINE.Byte(n, 1)); OutByte(MACHINE.Byte(n, 2)); OutByte(MACHINE.Byte(n, 3)) END OutInt; PROCEDURE isByte (n: INTEGER): BOOLEAN; RETURN (-128 <= n) & (n <= 127) END isByte; PROCEDURE short (n: INTEGER): INTEGER; RETURN 2 * ORD(isByte(n)) END short; PROCEDURE long (n: INTEGER): INTEGER; RETURN 40H * ORD(~isByte(n)) END long; PROCEDURE OutIntByte (n: INTEGER); BEGIN IF isByte(n) THEN OutByte(MACHINE.Byte(n, 0)) ELSE OutInt(n) END END OutIntByte; PROCEDURE isLong (n: INTEGER): BOOLEAN; RETURN (n > MACHINE.max32) OR (n < MACHINE.min32) END isLong; PROCEDURE NewNumber (value: INTEGER); VAR number: Number; BEGIN NEW(number); number.value := value; LISTS.push(Numbers, number); INC(Numbers_Count) END NewNumber; PROCEDURE NewLabel (): INTEGER; BEGIN BIN.NewLabel(prog) RETURN CODE.NewLabel() END NewLabel; PROCEDURE Rex (reg1, reg2: INTEGER); BEGIN OutByte(48H + reg1 DIV 8 + 4 * (reg2 DIV 8)) END Rex; PROCEDURE lea (reg, offset, section: INTEGER); BEGIN Rex(0, reg); OutByte2(8DH, 05H + 8 * (reg MOD 8)); // lea reg, [rip + offset] X86.Reloc(section, offset) END lea; PROCEDURE oprr (op: BYTE; reg1, reg2: INTEGER); // op reg1, reg2 BEGIN Rex(reg1, reg2); OutByte2(op, 0C0H + 8 * (reg2 MOD 8) + reg1 MOD 8) END oprr; PROCEDURE oprr2 (op1, op2: BYTE; reg1, reg2: INTEGER); // op reg1, reg2 BEGIN Rex(reg1, reg2); OutByte3(op1, op2, 0C0H + 8 * (reg2 MOD 8) + reg1 MOD 8) END oprr2; PROCEDURE mov (reg1, reg2: INTEGER); // mov reg1, reg2 BEGIN oprr(89H, reg1, reg2) END mov; PROCEDURE xor (reg1, reg2: INTEGER); // xor reg1, reg2 BEGIN oprr(31H, reg1, reg2) END xor; PROCEDURE and (reg1, reg2: INTEGER); // and reg1, reg2 BEGIN oprr(21H, reg1, reg2) END and; PROCEDURE or (reg1, reg2: INTEGER); // and reg1, reg2 BEGIN oprr(09H, reg1, reg2) END or; PROCEDURE add (reg1, reg2: INTEGER); // add reg1, reg2 BEGIN oprr(01H, reg1, reg2) END add; PROCEDURE sub (reg1, reg2: INTEGER); // sub reg1, reg2 BEGIN oprr(29H, reg1, reg2) END sub; PROCEDURE xchg (reg1, reg2: INTEGER); // xchg reg1, reg2 BEGIN oprr(87H, reg1, reg2) END xchg; PROCEDURE cmprr (reg1, reg2: INTEGER); // cmp reg1, reg2 BEGIN oprr(39H, reg1, reg2) END cmprr; PROCEDURE pop (reg: INTEGER); // pop reg BEGIN IF reg >= 8 THEN OutByte(41H) END; OutByte(58H + reg MOD 8) END pop; PROCEDURE push (reg: INTEGER); // push reg BEGIN IF reg >= 8 THEN OutByte(41H) END; OutByte(50H + reg MOD 8) END push; PROCEDURE decr (reg: INTEGER); BEGIN Rex(reg, 0); OutByte2(0FFH, 0C8H + reg MOD 8) // dec reg1 END decr; PROCEDURE incr (reg: INTEGER); BEGIN Rex(reg, 0); OutByte2(0FFH, 0C0H + reg MOD 8) // inc reg1 END incr; PROCEDURE drop; BEGIN REG.Drop(R) END drop; PROCEDURE callimp (label: INTEGER); VAR reg: INTEGER; BEGIN reg := REG.GetAnyReg(R); lea(reg, label, sIMP); IF reg >= 8 THEN // call qword[reg] OutByte(41H) END; OutByte2(0FFH, 10H + reg MOD 8); drop END callimp; PROCEDURE pushDA (offs: INTEGER); VAR reg: INTEGER; BEGIN reg := REG.GetAnyReg(R); lea(reg, offs, sDATA); push(reg); drop END pushDA; PROCEDURE CallRTL (proc: INTEGER); VAR label: INTEGER; BEGIN REG.Store(R); label := CODE.codes.rtl[proc]; IF label < 0 THEN callimp(-label) ELSE X86.call(label) END; REG.Restore(R) END CallRTL; PROCEDURE UnOp (VAR reg: INTEGER); BEGIN REG.UnOp(R, reg) END UnOp; PROCEDURE BinOp (VAR reg1, reg2: INTEGER); BEGIN REG.BinOp(R, reg1, reg2) END BinOp; PROCEDURE PushAll (NumberOfParameters: INTEGER); BEGIN REG.PushAll(R); R.pushed := R.pushed - NumberOfParameters END PushAll; PROCEDURE movabs (reg, n: INTEGER); VAR i: INTEGER; BEGIN Rex(reg, 0); OutByte(0B8H + reg MOD 8); // movabs reg, n FOR i := 0 TO 7 DO OutByte(MACHINE.Byte(n, i)) END END movabs; PROCEDURE movrc (reg, n: INTEGER); // mov reg, n BEGIN IF isLong(n) THEN movabs(reg, n) ELSE Rex(reg, 0); OutByte2(0C7H, 0C0H + reg MOD 8); OutInt(n) END END movrc; PROCEDURE test (reg: INTEGER); // test reg, reg BEGIN oprr(85H, reg, reg) END test; PROCEDURE oprlongc (reg, n: INTEGER; oprr: OPRR); VAR reg2: INTEGER; BEGIN reg2 := REG.GetAnyReg(R); movabs(reg2, n); oprr(reg, reg2); drop END oprlongc; PROCEDURE oprc (op, reg, n: INTEGER; oprr: OPRR); BEGIN IF isLong(n) THEN oprlongc(reg, n, oprr) ELSE Rex(reg, 0); OutByte2(81H + short(n), op + reg MOD 8); OutIntByte(n) END END oprc; PROCEDURE cmprc (reg, n: INTEGER); // cmp reg, n BEGIN oprc(0F8H, reg, n, cmprr) END cmprc; PROCEDURE addrc (reg, n: INTEGER); // add reg, n BEGIN oprc(0C0H, reg, n, add) END addrc; PROCEDURE subrc (reg, n: INTEGER); // sub reg, n BEGIN oprc(0E8H, reg, n, sub) END subrc; PROCEDURE andrc (reg, n: INTEGER); // and reg, n BEGIN oprc(0E0H, reg, n, and) END andrc; PROCEDURE pushc (n: INTEGER); VAR reg2: INTEGER; BEGIN IF isLong(n) THEN reg2 := REG.GetAnyReg(R); movabs(reg2, n); push(reg2); drop ELSE OutByte(68H + short(n)); OutIntByte(n) // push n END END pushc; PROCEDURE not (reg: INTEGER); // not reg BEGIN Rex(reg, 0); OutByte2(0F7H, 0D0H + reg MOD 8) END not; PROCEDURE neg (reg: INTEGER); // neg reg BEGIN Rex(reg, 0); OutByte2(0F7H, 0D8H + reg MOD 8) END neg; PROCEDURE movzx (reg1, reg2, offs: INTEGER; word: BOOLEAN); // movzx reg1, byte/word[reg2 + offs] VAR b: BYTE; BEGIN Rex(reg2, reg1); OutByte2(0FH, 0B6H + ORD(word)); IF (offs = 0) & (reg2 # rbp) THEN b := 0 ELSE b := 40H + long(offs) END; OutByte(b + (reg1 MOD 8) * 8 + reg2 MOD 8); IF reg2 = rsp THEN OutByte(24H) END; IF b # 0 THEN OutIntByte(offs) END END movzx; PROCEDURE _movrm (reg1, reg2, offs, size: INTEGER; mr: BOOLEAN); VAR b: BYTE; BEGIN IF size = 16 THEN OutByte(66H) END; IF (reg1 >= 8) OR (reg2 >= 8) OR (size = 64) THEN OutByte(40H + reg2 DIV 8 + 4 * (reg1 DIV 8) + 8 * ORD(size = 64)) END; OutByte(8BH - 2 * ORD(mr) - ORD(size = 8)); IF (offs = 0) & (reg2 # rbp) THEN b := 0 ELSE b := 40H + long(offs) END; OutByte(b + (reg1 MOD 8) * 8 + reg2 MOD 8); IF reg2 = rsp THEN OutByte(24H) END; IF b # 0 THEN OutIntByte(offs) END END _movrm; PROCEDURE movmr32 (reg1, offs, reg2: INTEGER); // mov dword[reg1+offs], reg2_32 BEGIN _movrm(reg2, reg1, offs, 32, TRUE) END movmr32; PROCEDURE movrm32 (reg1, reg2, offs: INTEGER); // mov reg1_32, dword[reg2+offs] BEGIN _movrm(reg1, reg2, offs, 32, FALSE) END movrm32; PROCEDURE movmr8 (reg1, offs, reg2: INTEGER); // mov byte[reg1+offs], reg2_8 BEGIN _movrm(reg2, reg1, offs, 8, TRUE) END movmr8; PROCEDURE movrm8 (reg1, reg2, offs: INTEGER); // mov reg1_8, byte[reg2+offs] BEGIN _movrm(reg1, reg2, offs, 8, FALSE) END movrm8; PROCEDURE movmr16 (reg1, offs, reg2: INTEGER); // mov word[reg1+offs], reg2_16 BEGIN _movrm(reg2, reg1, offs, 16, TRUE) END movmr16; PROCEDURE movrm16 (reg1, reg2, offs: INTEGER); // mov reg1_16, word[reg2+offs] BEGIN _movrm(reg1, reg2, offs, 16, FALSE) END movrm16; PROCEDURE movmr (reg1, offs, reg2: INTEGER); // mov qword[reg1+offs], reg2 BEGIN _movrm(reg2, reg1, offs, 64, TRUE) END movmr; PROCEDURE movrm (reg1, reg2, offs: INTEGER); // mov reg1, qword[reg2+offs] BEGIN _movrm(reg1, reg2, offs, 64, FALSE) END movrm; PROCEDURE pushm (reg, offs: INTEGER); // push qword[reg+offs] VAR b: BYTE; BEGIN IF reg >= 8 THEN OutByte(41H) END; OutByte(0FFH); IF (offs = 0) & (reg # rbp) THEN b := 30H ELSE b := 70H + long(offs) END; OutByte(b + reg MOD 8); IF reg = rsp THEN OutByte(24H) END; IF b # 30H THEN OutIntByte(offs) END END pushm; PROCEDURE comisd (xmm1, xmm2: INTEGER); // comisd xmm1, xmm2 BEGIN OutByte(66H); IF (xmm1 >= 8) OR (xmm2 >= 8) THEN OutByte(40H + (xmm1 DIV 8) * 4 + xmm2 DIV 8) END; OutByte3(0FH, 2FH, 0C0H + (xmm1 MOD 8) * 8 + xmm2 MOD 8) END comisd; PROCEDURE _movsdrm (xmm, reg, offs: INTEGER; mr: BOOLEAN); VAR b: BYTE; BEGIN OutByte(0F2H); IF (xmm >= 8) OR (reg >= 8) THEN OutByte(40H + (xmm DIV 8) * 4 + reg DIV 8) END; OutByte2(0FH, 10H + ORD(mr)); IF (offs = 0) & (reg # rbp) THEN b := 0 ELSE b := 40H + long(offs) END; OutByte(b + (xmm MOD 8) * 8 + reg MOD 8); IF reg = rsp THEN OutByte(24H) END; IF b # 0 THEN OutIntByte(offs) END END _movsdrm; PROCEDURE movsdrm (xmm, reg, offs: INTEGER); // movsd xmm, qword[reg+offs] BEGIN _movsdrm(xmm, reg, offs, FALSE) END movsdrm; PROCEDURE movsdmr (reg, offs, xmm: INTEGER); // movsd qword[reg+offs], xmm BEGIN _movsdrm(xmm, reg, offs, TRUE) END movsdmr; PROCEDURE opxx (op, xmm1, xmm2: INTEGER); BEGIN OutByte(0F2H); IF (xmm1 >= 8) OR (xmm2 >= 8) THEN OutByte(40H + (xmm1 DIV 8) * 4 + xmm2 DIV 8) END; OutByte3(0FH, op, 0C0H + (xmm1 MOD 8) * 8 + xmm2 MOD 8) END opxx; PROCEDURE jcc (cc, label: INTEGER); // jcc label BEGIN X86.jcc(cc, label) END jcc; PROCEDURE jmp (label: INTEGER); // jmp label BEGIN X86.jmp(label) END jmp; PROCEDURE setcc (cc, reg: INTEGER); //setcc reg8 BEGIN IF reg >= 8 THEN OutByte(41H) END; OutByte3(0FH, cc, 0C0H + reg MOD 8) END setcc; PROCEDURE shiftrc (op, reg, n: INTEGER); BEGIN Rex(reg, 0); IF n = 1 THEN OutByte(0D1H) ELSE OutByte(0C1H) END; X86.shift(op, reg MOD 8); IF n # 1 THEN OutByte(n) END END shiftrc; PROCEDURE getVar (variables: LISTS.LIST; offset: INTEGER): CODE.LOCALVAR; VAR cur: CODE.LOCALVAR; BEGIN cur := variables.first(CODE.LOCALVAR); WHILE (cur # NIL) & (cur.offset # offset) DO cur := cur.next(CODE.LOCALVAR) END RETURN cur END getVar; PROCEDURE allocReg (cmd: COMMAND); VAR leave: BOOLEAN; leaf: BOOLEAN; cur: COMMAND; variables: LISTS.LIST; lvar, rvar: CODE.LOCALVAR; reg: INTEGER; max: INTEGER; loop: INTEGER; BEGIN loop := 1; variables := cmd.variables; leave := FALSE; leaf := TRUE; cur := cmd.next(COMMAND); REPEAT CASE cur.opcode OF |CODE.opLLOAD64, CODE.opLLOAD8, CODE.opLLOAD16, CODE.opLLOAD32, CODE.opLLOAD64_PARAM, CODE.opLLOAD32_PARAM, CODE.opLADR_SAVE, CODE.opLADR_INC1, CODE.opLADR_DEC1, CODE.opLADR_INC, CODE.opLADR_DEC, CODE.opLADR_INC1B, CODE.opLADR_DEC1B, CODE.opLADR_INCB, CODE.opLADR_DECB, CODE.opLADR_INCL, CODE.opLADR_EXCL, CODE.opLADR_UNPK: lvar := getVar(variables, cur.param2); IF (lvar # NIL) & (lvar.count # -1) THEN INC(lvar.count, loop) END |CODE.opLADR_SAVEC, CODE.opLADR_INCC, CODE.opLADR_DECC, CODE.opLADR_INCCB, CODE.opLADR_DECCB, CODE.opLADR_INCLC, CODE.opLADR_EXCLC: lvar := getVar(variables, cur.param1); IF (lvar # NIL) & (lvar.count # -1) THEN INC(lvar.count, loop) END |CODE.opLADR: lvar := getVar(variables, cur.param2); IF (lvar # NIL) & (lvar.count # -1) THEN lvar.count := -1 END |CODE.opLOOP: INC(loop, 10) |CODE.opENDLOOP: DEC(loop, 10) |CODE.opLEAVE, CODE.opLEAVER, CODE.opLEAVEF: leave := TRUE |CODE.opCALL, CODE.opCALLP, CODE.opCALLI, CODE.opWIN64CALL, CODE.opWIN64CALLP, CODE.opWIN64CALLI, CODE.opSYSVCALL, CODE.opSYSVCALLP, CODE.opSYSVCALLI: leaf := FALSE ELSE END; cur := cur.next(COMMAND) UNTIL leave OR ~leaf; IF leaf THEN REPEAT reg := -1; max := -1; rvar := NIL; lvar := variables.first(CODE.LOCALVAR); WHILE lvar # NIL DO IF lvar.count > max THEN max := lvar.count; rvar := lvar END; lvar := lvar.next(CODE.LOCALVAR) END; IF rvar # NIL THEN reg := REG.GetAnyVarReg(R); IF reg # -1 THEN REG.Lock(R, reg, rvar.offset, rvar.size); REG.Load(R, reg); rvar.count := -1 END END UNTIL (rvar = NIL) OR (reg = -1) END END allocReg; PROCEDURE GetRegA; BEGIN ASSERT(REG.GetReg(R, rax)) END GetRegA; PROCEDURE Win64Passing (params: INTEGER); VAR n, i: INTEGER; BEGIN n := params MOD 32; params := params DIV 32; FOR i := 0 TO n - 1 DO IF i IN BITS(params) THEN movsdrm(i, rsp, i * 8) ELSE movrm(Win64RegPar[i], rsp, i * 8) END END END Win64Passing; PROCEDURE SysVPassing (params: INTEGER); VAR n, i, s, p, ofs: INTEGER; i_count, f_count: INTEGER; reg: BOOLEAN; BEGIN ASSERT(r10 IN R.regs); n := params MOD 32; params := params DIV 32; s := 0; i_count := 0; f_count := 0; FOR i := 0 TO n - 1 DO IF i IN BITS(params) THEN INC(f_count) ELSE INC(i_count) END END; s := MAX(0, f_count - 8) + MAX(0, i_count - 6); p := 0; subrc(rsp, s * 8); i_count := 0; f_count := 0; FOR i := 0 TO n - 1 DO ofs := (i + s) * 8; IF i IN BITS(params) THEN reg := f_count <= 7; IF reg THEN movsdrm(f_count, rsp, ofs); INC(f_count) END ELSE reg := i_count <= 5; IF reg THEN movrm(SystemVRegPar[i_count], rsp, ofs); INC(i_count) END END; IF ~reg THEN movrm(r10, rsp, ofs); movmr(rsp, p, r10); INC(p, 8) END END END SysVPassing; PROCEDURE fcmp (op: INTEGER; xmm: INTEGER); VAR cc, reg: INTEGER; BEGIN reg := REG.GetAnyReg(R); xor(reg, reg); CASE op OF |CODE.opEQF, CODE.opEQFI: comisd(xmm - 1, xmm); cc := sete |CODE.opNEF, CODE.opNEFI: comisd(xmm - 1, xmm); cc := setne |CODE.opLTF, CODE.opGTFI: comisd(xmm - 1, xmm); cc := setc |CODE.opGTF, CODE.opLTFI: comisd(xmm, xmm - 1); cc := setc |CODE.opLEF, CODE.opGEFI: comisd(xmm, xmm - 1); cc := setnc |CODE.opGEF, CODE.opLEFI: comisd(xmm - 1, xmm); cc := setnc END; OutByte2(7AH, 3 + reg DIV 8); // jp L setcc(cc, reg); //L: END fcmp; PROCEDURE translate (commands: LISTS.LIST; stroffs: INTEGER); VAR cmd, next: COMMAND; param1, param2, param3, a, b, c, n, label, L, i, cc: INTEGER; reg1, reg2, xmm: INTEGER; float: REAL; regVar: BOOLEAN; BEGIN xmm := -1; cmd := commands.first(COMMAND); WHILE cmd # NIL DO param1 := cmd.param1; param2 := cmd.param2; CASE cmd.opcode OF |CODE.opJMP: jmp(param1) |CODE.opCALL, CODE.opWIN64CALL, CODE.opSYSVCALL: REG.Store(R); CASE cmd.opcode OF |CODE.opCALL: |CODE.opWIN64CALL: Win64Passing(param2) |CODE.opSYSVCALL: SysVPassing(param2) END; X86.call(param1); REG.Restore(R) |CODE.opCALLP, CODE.opWIN64CALLP, CODE.opSYSVCALLP: UnOp(reg1); IF reg1 # rax THEN GetRegA; ASSERT(REG.Exchange(R, reg1, rax)); drop END; drop; REG.Store(R); CASE cmd.opcode OF |CODE.opCALLP: |CODE.opWIN64CALLP: Win64Passing(param2) |CODE.opSYSVCALLP: SysVPassing(param2) END; OutByte2(0FFH, 0D0H); // call rax REG.Restore(R); ASSERT(R.top = -1) |CODE.opCALLI, CODE.opWIN64CALLI, CODE.opSYSVCALLI: REG.Store(R); CASE cmd.opcode OF |CODE.opCALLI: |CODE.opWIN64CALLI: Win64Passing(param2) |CODE.opSYSVCALLI: SysVPassing(param2) END; callimp(param1); REG.Restore(R) |CODE.opLABEL: X86.SetLabel(param2) |CODE.opERR: CallRTL(CODE._error) |CODE.opERRC: pushc(param2) |CODE.opPRECALL: n := param2; IF (param1 # 0) & (n # 0) THEN subrc(rsp, 8) END; WHILE n > 0 DO subrc(rsp, 8); movsdmr(rsp, 0, xmm); DEC(xmm); DEC(n) END; ASSERT(xmm = -1); PushAll(0) |CODE.opWIN64ALIGN16: ASSERT(rax IN R.regs); mov(rax, rsp); andrc(rsp, -16); push(rax); subrc(rsp, (MAX(param2 - 4, 0) MOD 2 + MAX(4 - param2, 0) + 1) * 8) |CODE.opSYSVALIGN16: ASSERT(rax IN R.regs); mov(rax, rsp); andrc(rsp, -16); push(rax); IF ~ODD(param2) THEN push(rax) END |CODE.opRESF: ASSERT(xmm = -1); INC(xmm); n := param2; IF n > 0 THEN movsdmr(rsp, n * 8, xmm); DEC(xmm); INC(n) END; WHILE n > 0 DO INC(xmm); movsdrm(xmm, rsp, 0); addrc(rsp, 8); DEC(n) END |CODE.opRES: ASSERT(R.top = -1); GetRegA; n := param2; WHILE n > 0 DO INC(xmm); movsdrm(xmm, rsp, 0); addrc(rsp, 8); DEC(n) END |CODE.opENTER: ASSERT(R.top = -1); X86.SetLabel(param1); param3 := cmd.param3; IF param3 > 0 THEN push(rbp); mov(rbp, rsp); n := param3 MOD 32; param3 := param3 DIV 32; FOR i := 0 TO n - 1 DO IF i IN BITS(param3) THEN movsdmr(rbp, i * 8 + 16, i) ELSE movmr(rbp, i * 8 + 16, Win64RegPar[i]) END END ELSIF param3 < 0 THEN param3 := -param3; n := (param3 MOD 32) * 8; param3 := param3 DIV 32; pop(r10); subrc(rsp, n); push(r10); push(rbp); mov(rbp, rsp); a := 0; b := 0; c := 0; INC(n, 16); FOR i := 16 TO n - 8 BY 8 DO IF ODD(param3) THEN IF b <= 7 THEN movsdmr(rbp, i, b); INC(b) ELSE movrm(r10, rbp, n + c); movmr(rbp, i, r10); INC(c, 8) END ELSE IF a <= 5 THEN movmr(rbp, i, SystemVRegPar[a]); INC(a) ELSE movrm(r10, rbp, n + c); movmr(rbp, i, r10); INC(c, 8) END END; param3 := param3 DIV 2 END ELSE push(rbp); mov(rbp, rsp) END; n := param2; IF n > 4 THEN movrc(rcx, n); // L: pushc(0); OutByte2(0E2H, 0FCH) // loop L ELSE WHILE n > 0 DO pushc(0); DEC(n) END END; IF cmd.allocReg THEN allocReg(cmd) END |CODE.opLEAVE, CODE.opLEAVER, CODE.opLEAVEF: IF cmd.opcode = CODE.opLEAVER THEN UnOp(reg1); IF reg1 # rax THEN GetRegA; ASSERT(REG.Exchange(R, reg1, rax)); drop END; drop END; ASSERT(R.top = -1); IF cmd.opcode = CODE.opLEAVEF THEN DEC(xmm) END; ASSERT(xmm = -1); mov(rsp, rbp); pop(rbp); IF param2 > 0 THEN OutByte3(0C2H, (param2 * 8) MOD 256, (param2 * 8) DIV 256) // ret param2 ELSE OutByte(0C3H) // ret END; REG.Reset(R) |CODE.opSAVES: UnOp(reg1); drop; PushAll(0); push(reg1); pushDA(stroffs + param2); pushc(param1); CallRTL(CODE._move) |CODE.opSADR: reg1 := REG.GetAnyReg(R); lea(reg1, stroffs + param2, sDATA) |CODE.opLOAD8: UnOp(reg1); movzx(reg1, reg1, 0, FALSE) |CODE.opLOAD16: UnOp(reg1); movzx(reg1, reg1, 0, TRUE) |CODE.opLOAD32: UnOp(reg1); movrm32(reg1, reg1, 0); shiftrc(shl, reg1, 32); shiftrc(shr, reg1, 32) |CODE.opLOAD64: UnOp(reg1); movrm(reg1, reg1, 0) |CODE.opLLOAD64: reg1 := REG.GetAnyReg(R); reg2 := REG.GetVarReg(R, param2); IF reg2 # -1 THEN mov(reg1, reg2) ELSE movrm(reg1, rbp, param2 * 8) END |CODE.opLLOAD8, CODE.opLLOAD16: reg1 := REG.GetAnyReg(R); reg2 := REG.GetVarReg(R, param2); IF reg2 # -1 THEN mov(reg1, reg2) ELSE movzx(reg1, rbp, param2 * 8, cmd.opcode = CODE.opLLOAD16) END |CODE.opLLOAD32: reg1 := REG.GetAnyReg(R); reg2 := REG.GetVarReg(R, param2); IF reg2 # -1 THEN mov(reg1, reg2) ELSE n := param2 * 8; xor(reg1, reg1); movrm32(reg1, rbp, n) END |CODE.opGLOAD64: reg1 := REG.GetAnyReg(R); lea(reg1, param2, sBSS); movrm(reg1, reg1, 0) |CODE.opGLOAD8: reg1 := REG.GetAnyReg(R); lea(reg1, param2, sBSS); movzx(reg1, reg1, 0, FALSE) |CODE.opGLOAD16: reg1 := REG.GetAnyReg(R); lea(reg1, param2, sBSS); movzx(reg1, reg1, 0, TRUE) |CODE.opGLOAD32: reg1 := REG.GetAnyReg(R); xor(reg1, reg1); lea(reg1, param2, sBSS); movrm32(reg1, reg1, 0) |CODE.opVLOAD64: reg1 := REG.GetAnyReg(R); movrm(reg1, rbp, param2 * 8); movrm(reg1, reg1, 0) |CODE.opVLOAD8, CODE.opVLOAD16: reg1 := REG.GetAnyReg(R); movrm(reg1, rbp, param2 * 8); movzx(reg1, reg1, 0, cmd.opcode = CODE.opVLOAD16) |CODE.opVLOAD32: reg1 := REG.GetAnyReg(R); reg2 := REG.GetAnyReg(R); xor(reg1, reg1); movrm(reg2, rbp, param2 * 8); movrm32(reg1, reg2, 0); drop |CODE.opLADR: n := param2 * 8; next := cmd.next(COMMAND); IF next.opcode = CODE.opSAVEF THEN movsdmr(rbp, n, xmm); DEC(xmm); cmd := next ELSIF next.opcode = CODE.opLOADF THEN INC(xmm); movsdrm(xmm, rbp, n); cmd := next ELSE reg1 := REG.GetAnyReg(R); Rex(0, reg1); OutByte2(8DH, 45H + long(n) + (reg1 MOD 8) * 8); // lea reg1, qword[rbp+n] OutIntByte(n) END |CODE.opGADR: reg1 := REG.GetAnyReg(R); lea(reg1, param2, sBSS) |CODE.opVADR: reg1 := REG.GetAnyReg(R); movrm(reg1, rbp, param2 * 8) |CODE.opSAVE8C: UnOp(reg1); IF reg1 >= 8 THEN OutByte(41H) END; OutByte3(0C6H, reg1 MOD 8, param2); // mov byte[reg1], param2 drop |CODE.opSAVE16C: UnOp(reg1); OutByte(66H); IF reg1 >= 8 THEN OutByte(41H) END; OutByte2(0C7H, reg1 MOD 8); OutByte2(param2 MOD 256, param2 DIV 256); // mov word[reg1], param2 drop |CODE.opSAVEC: UnOp(reg1); IF isLong(param2) THEN reg2 := REG.GetAnyReg(R); movrc(reg2, param2); movmr(reg1, 0, reg2); drop ELSE Rex(reg1, 0); OutByte2(0C7H, reg1 MOD 8); // mov qword[reg1], param2 OutInt(param2) END; drop |CODE.opRSET: PushAll(2); CallRTL(CODE._set); GetRegA |CODE.opRSETR: PushAll(1); pushc(param2); CallRTL(CODE._set); GetRegA |CODE.opRSETL: PushAll(1); pushc(param2); CallRTL(CODE._set2); GetRegA |CODE.opRSET1: UnOp(reg1); PushAll(1); push(reg1); CallRTL(CODE._set); GetRegA |CODE.opINCL, CODE.opEXCL: BinOp(reg1, reg2); cmprc(reg1, 64); OutByte2(73H, 04H); // jnb L Rex(reg2, reg1); OutByte3(0FH, 0ABH + 8 * ORD(cmd.opcode = CODE.opEXCL), 8 * (reg1 MOD 8) + reg2 MOD 8); // bts/btr qword[reg2], reg1 // L: drop; drop |CODE.opINCLC, CODE.opEXCLC: UnOp(reg1); Rex(reg1, 0); OutByte2(0FH, 0BAH); // bts/btr qword[reg1], param2 OutByte2(28H + 8 * ORD(cmd.opcode = CODE.opEXCLC) + reg1 MOD 8, param2); drop |CODE.opEQS .. CODE.opGES: PushAll(4); pushc(cmd.opcode - CODE.opEQS); CallRTL(CODE._strcmp); GetRegA |CODE.opEQS2 .. CODE.opGES2: PushAll(4); pushc(cmd.opcode - CODE.opEQS2); CallRTL(CODE._strcmp2); GetRegA |CODE.opEQSW .. CODE.opGESW: PushAll(4); pushc(cmd.opcode - CODE.opEQSW); CallRTL(CODE._strcmpw); GetRegA |CODE.opEQSW2 .. CODE.opGESW2: PushAll(4); pushc(cmd.opcode - CODE.opEQSW2); CallRTL(CODE._strcmpw2); GetRegA |CODE.opINC1, CODE.opDEC1: UnOp(reg1); Rex(reg1, 0); OutByte2(0FFH, reg1 MOD 8 + 8 * ORD(cmd.opcode = CODE.opDEC1)); drop |CODE.opCONST: reg1 := REG.GetAnyReg(R); movrc(reg1, param2) |CODE.opGT, CODE.opGE, CODE.opLT, CODE.opLE, CODE.opEQ, CODE.opNE: BinOp(reg1, reg2); cmprr(reg1, reg2); drop; drop; cc := X86.cond(cmd.opcode); IF cmd.next(COMMAND).opcode = CODE.opJE THEN label := cmd.next(COMMAND).param1; jcc(cc, label); cmd := cmd.next(COMMAND) ELSIF cmd.next(COMMAND).opcode = CODE.opJNE THEN label := cmd.next(COMMAND).param1; jcc(X86.inv1(cc), label); cmd := cmd.next(COMMAND) ELSE reg1 := REG.GetAnyReg(R); setcc(cc + 16, reg1); andrc(reg1, 1) END |CODE.opGTR, CODE.opLTL, CODE.opGER, CODE.opLEL, CODE.opLER, CODE.opGEL, CODE.opLTR, CODE.opGTL, CODE.opEQR, CODE.opEQL, CODE.opNER, CODE.opNEL: UnOp(reg1); IF param2 = 0 THEN test(reg1) ELSE cmprc(reg1, param2) END; drop; cc := X86.cond(cmd.opcode); IF cmd.next(COMMAND).opcode = CODE.opJE THEN label := cmd.next(COMMAND).param1; jcc(cc, label); cmd := cmd.next(COMMAND) ELSIF cmd.next(COMMAND).opcode = CODE.opJNE THEN label := cmd.next(COMMAND).param1; jcc(X86.inv1(cc), label); cmd := cmd.next(COMMAND) ELSE reg1 := REG.GetAnyReg(R); setcc(cc + 16, reg1); andrc(reg1, 1) END |CODE.opCODE: OutByte(param2) |CODE.opPUSHIP: reg1 := REG.GetAnyReg(R); lea(reg1, param2, sIMP); movrm(reg1, reg1, 0) |CODE.opPARAM: n := param2; IF n = 1 THEN UnOp(reg1); push(reg1); drop ELSE ASSERT(R.top + 1 <= n); PushAll(n) END |CODE.opJNZ: UnOp(reg1); test(reg1); jcc(jne, param1) |CODE.opJZ: UnOp(reg1); test(reg1); jcc(je, param1) |CODE.opJE: UnOp(reg1); test(reg1); jcc(jne, param1); drop |CODE.opJNE: UnOp(reg1); test(reg1); jcc(je, param1); drop |CODE.opIN: label := NewLabel(); L := NewLabel(); BinOp(reg1, reg2); cmprc(reg1, 64); jcc(jb, L); xor(reg1, reg1); jmp(label); X86.SetLabel(L); Rex(reg2, reg1); OutByte3(0FH, 0A3H, 0C0H + 8 * (reg1 MOD 8) + reg2 MOD 8); // bt reg2, reg1 setcc(setc, reg1); andrc(reg1, 1); X86.SetLabel(label); drop |CODE.opINR: label := NewLabel(); L := NewLabel(); UnOp(reg1); reg2 := REG.GetAnyReg(R); cmprc(reg1, 64); jcc(jb, L); xor(reg1, reg1); jmp(label); X86.SetLabel(L); movrc(reg2, param2); Rex(reg2, reg1); OutByte3(0FH, 0A3H, 0C0H + 8 * (reg1 MOD 8) + reg2 MOD 8); // bt reg2, reg1 setcc(setc, reg1); andrc(reg1, 1); X86.SetLabel(label); drop |CODE.opINL: UnOp(reg1); Rex(reg1, 0); OutByte2(0FH, 0BAH); // bt reg1, param2 OutByte2(0E0H + reg1 MOD 8, param2); setcc(setc, reg1); andrc(reg1, 1) |CODE.opNOT: UnOp(reg1); test(reg1); setcc(sete, reg1); andrc(reg1, 1) |CODE.opORD: UnOp(reg1); test(reg1); setcc(setne, reg1); andrc(reg1, 1) |CODE.opABS: UnOp(reg1); test(reg1); OutByte2(7DH, 03H); // jge L neg(reg1) // L: |CODE.opEQB, CODE.opNEB: BinOp(reg1, reg2); drop; drop; test(reg1); OutByte2(74H, 07H); // je L1 movrc(reg1, 1); // L1: test(reg2); OutByte2(74H, 07H); // je L2 movrc(reg2, 1); // L2: cmprr(reg1, reg2); reg1 := REG.GetAnyReg(R); IF cmd.opcode = CODE.opEQB THEN setcc(sete, reg1) ELSE setcc(setne, reg1) END; andrc(reg1, 1) |CODE.opMULSC: UnOp(reg1); andrc(reg1, param2) |CODE.opDIVSC, CODE.opADDSL, CODE.opADDSR: UnOp(reg1); Rex(reg1, 0); OutByte2(81H + short(param2), 0C8H + 28H * ORD(cmd.opcode = CODE.opDIVSC) + reg1 MOD 8); // or/xor reg1, param2 OutIntByte(param2) |CODE.opSUBSL: UnOp(reg1); not(reg1); andrc(reg1, param2) |CODE.opSUBSR: UnOp(reg1); andrc(reg1, ORD(-BITS(param2))) |CODE.opMULS: BinOp(reg1, reg2); and(reg1, reg2); drop |CODE.opDIVS: BinOp(reg1, reg2); xor(reg1, reg2); drop |CODE.opUMINS: UnOp(reg1); not(reg1) |CODE.opCOPY: PushAll(2); pushc(param2); CallRTL(CODE._move2) |CODE.opMOVE: PushAll(3); CallRTL(CODE._move2) |CODE.opCOPYA: PushAll(4); pushc(param2); CallRTL(CODE._arrcpy); GetRegA |CODE.opCOPYS: PushAll(4); pushc(param2); CallRTL(CODE._strcpy) |CODE.opCOPYS2: PushAll(4); pushc(param2); CallRTL(CODE._strcpy2) |CODE.opROT: PushAll(0); push(rsp); pushc(param2); CallRTL(CODE._rot) |CODE.opNEW: PushAll(1); n := param2 + 16; ASSERT(MACHINE.Align(n, 64)); pushc(n); pushc(param1); CallRTL(CODE._new) |CODE.opDISP: PushAll(1); CallRTL(CODE._dispose) |CODE.opPUSHT: UnOp(reg1); reg2 := REG.GetAnyReg(R); movrm(reg2, reg1, -8) |CODE.opISREC: PushAll(2); pushc(param2); CallRTL(CODE._isrec); GetRegA |CODE.opIS: PushAll(1); pushc(param2); CallRTL(CODE._is); GetRegA |CODE.opTYPEGR: PushAll(1); pushc(param2); CallRTL(CODE._guardrec); GetRegA |CODE.opTYPEGP: UnOp(reg1); PushAll(0); push(reg1); pushc(param2); CallRTL(CODE._guard); GetRegA |CODE.opTYPEGD: UnOp(reg1); PushAll(0); pushm(reg1, -8); pushc(param2); CallRTL(CODE._guardrec); GetRegA |CODE.opCASET: push(r10); push(r10); pushc(param2); CallRTL(CODE._guardrec); pop(r10); test(rax); jcc(jne, param1) |CODE.opSAVEP: UnOp(reg1); reg2 := REG.GetAnyReg(R); lea(reg2, param2, sCODE); movmr(reg1, 0, reg2); drop; drop |CODE.opPUSHP: reg1 := REG.GetAnyReg(R); lea(reg1, param2, sCODE) |CODE.opINC, CODE.opDEC: BinOp(reg1, reg2); // add/sub qword[reg2], reg1 Rex(reg2, reg1); OutByte2(01H + 28H * ORD(cmd.opcode = CODE.opDEC), reg2 MOD 8 + (reg1 MOD 8) * 8); drop; drop |CODE.opINCC, CODE.opDECC: UnOp(reg1); IF isLong(param2) THEN reg2 := REG.GetAnyReg(R); movrc(reg2, param2); // add/sub qword[reg1], reg2 Rex(reg1, reg2); OutByte2(01H + 28H * ORD(cmd.opcode = CODE.opDECC), reg1 MOD 8 + (reg2 MOD 8) * 8); drop ELSE // add/sub qword[reg1], param2 Rex(reg1, 0); OutByte2(81H + short(param2), 28H * ORD(cmd.opcode = CODE.opDECC) + reg1 MOD 8); OutIntByte(param2) END; drop |CODE.opDROP: UnOp(reg1); drop |CODE.opSAVE, CODE.opSAVE64: BinOp(reg2, reg1); movmr(reg1, 0, reg2); drop; drop |CODE.opSAVE8: BinOp(reg2, reg1); movmr8(reg1, 0, reg2); drop; drop |CODE.opSAVE16: BinOp(reg2, reg1); movmr16(reg1, 0, reg2); drop; drop |CODE.opSAVE32: BinOp(reg2, reg1); movmr32(reg1, 0, reg2); drop; drop |CODE.opMIN: BinOp(reg1, reg2); cmprr(reg1, reg2); OutByte2(7EH, 3); // jle L mov(reg1, reg2); // L: drop |CODE.opMAX: BinOp(reg1, reg2); cmprr(reg1, reg2); OutByte2(7DH, 3); // jge L mov(reg1, reg2); // L: drop |CODE.opMINC: UnOp(reg1); cmprc(reg1, param2); label := NewLabel(); jcc(jle, label); movrc(reg1, param2); X86.SetLabel(label) |CODE.opMAXC: UnOp(reg1); cmprc(reg1, param2); label := NewLabel(); jcc(jge, label); movrc(reg1, param2); X86.SetLabel(label) |CODE.opSBOOL: BinOp(reg2, reg1); test(reg2); setcc(setne, reg2); movmr8(reg1, 0, reg2); drop; drop |CODE.opSBOOLC: UnOp(reg1); IF reg1 >= 8 THEN OutByte(41H) END; OutByte3(0C6H, reg1 MOD 8, ORD(param2 # 0)); drop |CODE.opODD: UnOp(reg1); andrc(reg1, 1) |CODE.opUMINUS: UnOp(reg1); neg(reg1) |CODE.opADD: BinOp(reg1, reg2); add(reg1, reg2); drop |CODE.opSUB: BinOp(reg1, reg2); sub(reg1, reg2); drop |CODE.opSUBR, CODE.opSUBL: UnOp(reg1); n := param2; IF n = 1 THEN decr(reg1) ELSIF n = -1 THEN incr(reg1) ELSIF n # 0 THEN subrc(reg1, n) END; IF cmd.opcode = CODE.opSUBL THEN neg(reg1) END |CODE.opADDL, CODE.opADDR: IF param2 # 0 THEN UnOp(reg1); IF param2 = 1 THEN incr(reg1) ELSIF param2 = -1 THEN decr(reg1) ELSE addrc(reg1, param2) END END |CODE.opDIV: PushAll(2); CallRTL(CODE._div); GetRegA |CODE.opDIVR: a := param2; IF a > 1 THEN n := X86.log2(a) ELSIF a < -1 THEN n := X86.log2(-a) ELSE n := -1 END; IF a = 1 THEN ELSIF a = -1 THEN UnOp(reg1); neg(reg1) ELSE IF n > 0 THEN UnOp(reg1); IF a < 0 THEN reg2 := REG.GetAnyReg(R); mov(reg2, reg1); shiftrc(sar, reg1, n); sub(reg1, reg2); drop ELSE shiftrc(sar, reg1, n) END ELSE PushAll(1); pushc(param2); CallRTL(CODE._div); GetRegA END END |CODE.opDIVL: PushAll(1); pushc(param2); CallRTL(CODE._div2); GetRegA |CODE.opMOD: PushAll(2); CallRTL(CODE._mod); GetRegA |CODE.opMODR: a := param2; IF a > 1 THEN n := X86.log2(a) ELSIF a < -1 THEN n := X86.log2(-a) ELSE n := -1 END; IF ABS(a) = 1 THEN UnOp(reg1); xor(reg1, reg1) ELSE IF n > 0 THEN UnOp(reg1); andrc(reg1, ABS(a) - 1); IF a < 0 THEN test(reg1); label := NewLabel(); jcc(je, label); addrc(reg1, a); X86.SetLabel(label) END ELSE PushAll(1); pushc(param2); CallRTL(CODE._mod); GetRegA END END |CODE.opMODL: PushAll(1); pushc(param2); CallRTL(CODE._mod2); GetRegA |CODE.opMUL: BinOp(reg1, reg2); oprr2(0FH, 0AFH, reg2, reg1); // imul reg1, reg2 drop |CODE.opMULC: UnOp(reg1); a := param2; IF a > 1 THEN n := X86.log2(a) ELSIF a < -1 THEN n := X86.log2(-a) ELSE n := -1 END; IF a = 1 THEN ELSIF a = -1 THEN neg(reg1) ELSIF a = 0 THEN xor(reg1, reg1) ELSE IF n > 0 THEN IF a < 0 THEN neg(reg1) END; shiftrc(shl, reg1, n) ELSE // imul reg1, a Rex(reg1, reg1); OutByte2(69H + short(a), 0C0H + (reg1 MOD 8) * 9); OutIntByte(a) END END |CODE.opADDS: BinOp(reg1, reg2); or(reg1, reg2); drop |CODE.opSUBS: BinOp(reg1, reg2); not(reg2); and(reg1, reg2); drop |CODE.opNOP: |CODE.opSWITCH: UnOp(reg1); IF param2 = 0 THEN reg2 := rax ELSE reg2 := r10 END; IF reg1 # reg2 THEN ASSERT(REG.GetReg(R, reg2)); ASSERT(REG.Exchange(R, reg1, reg2)); drop END; drop |CODE.opENDSW: |CODE.opCASEL: cmprc(rax, param1); jcc(jl, param2) |CODE.opCASER: cmprc(rax, param1); jcc(jg, param2) |CODE.opCASELR: cmprc(rax, param1); jcc(jl, param2); jcc(jg, cmd.param3) |CODE.opASR, CODE.opROR, CODE.opLSL, CODE.opLSR: BinOp(reg1, reg2); xchg(reg2, rcx); Rex(reg1, 0); OutByte(0D3H); X86.shift(cmd.opcode, reg1 MOD 8); // shift reg1, cl xchg(reg2, rcx); drop |CODE.opASR1, CODE.opROR1, CODE.opLSL1, CODE.opLSR1: reg1 := REG.GetAnyReg(R); movrc(reg1, param2); BinOp(reg1, reg2); xchg(reg1, rcx); Rex(reg2, 0); OutByte(0D3H); X86.shift(cmd.opcode, reg2 MOD 8); // shift reg2, cl xchg(reg1, rcx); drop; drop; ASSERT(REG.GetReg(R, reg2)) |CODE.opASR2, CODE.opROR2, CODE.opLSL2, CODE.opLSR2: UnOp(reg1); shiftrc(cmd.opcode, reg1, ORD(BITS(param2) * {0..5})) |CODE.opGET: BinOp(reg1, reg2); drop; drop; _movrm(reg1, reg1, 0, param2 * 8, FALSE); _movrm(reg1, reg2, 0, param2 * 8, TRUE) |CODE.opCHKBYTE: BinOp(reg1, reg2); cmprc(reg1, 256); jcc(jb, param1) |CODE.opCHKIDX: UnOp(reg1); cmprc(reg1, param2); jcc(jb, param1) |CODE.opCHKIDX2: BinOp(reg1, reg2); IF param2 # -1 THEN cmprr(reg2, reg1); mov(reg1, reg2); drop; jcc(jb, param1) ELSE INCL(R.regs, reg1); DEC(R.top); R.stk[R.top] := reg2 END |CODE.opLENGTH: PushAll(2); CallRTL(CODE._length); GetRegA |CODE.opLENGTHW: PushAll(2); CallRTL(CODE._lengthw); GetRegA |CODE.opLEN: n := param2; UnOp(reg1); drop; EXCL(R.regs, reg1); WHILE n > 0 DO UnOp(reg2); drop; DEC(n) END; INCL(R.regs, reg1); ASSERT(REG.GetReg(R, reg1)) |CODE.opCHR: UnOp(reg1); andrc(reg1, 255) |CODE.opWCHR: UnOp(reg1); andrc(reg1, 65535) |CODE.opEQP, CODE.opNEP, CODE.opEQIP, CODE.opNEIP: UnOp(reg1); reg2 := REG.GetAnyReg(R); CASE cmd.opcode OF |CODE.opEQP, CODE.opNEP: lea(reg2, param1, sCODE) |CODE.opEQIP, CODE.opNEIP: lea(reg2, param1, sIMP); movrm(reg2, reg2, 0) END; cmprr(reg1, reg2); drop; drop; reg1 := REG.GetAnyReg(R); CASE cmd.opcode OF |CODE.opEQP, CODE.opEQIP: setcc(sete, reg1) |CODE.opNEP, CODE.opNEIP: setcc(setne, reg1) END; andrc(reg1, 1) |CODE.opINC1B, CODE.opDEC1B: UnOp(reg1); IF reg1 >= 8 THEN OutByte(41H) END; OutByte2(0FEH, 8 * ORD(cmd.opcode = CODE.opDEC1B) + reg1 MOD 8); // inc/dec byte[reg1] drop |CODE.opINCCB, CODE.opDECCB: UnOp(reg1); IF reg1 >= 8 THEN OutByte(41H) END; OutByte3(80H, 28H * ORD(cmd.opcode = CODE.opDECCB) + reg1 MOD 8, param2 MOD 256); // add/sub byte[reg1], param2 MOD 256 drop |CODE.opINCB, CODE.opDECB: BinOp(reg1, reg2); IF (reg1 >= 8) OR (reg2 >= 8) THEN OutByte(40H + reg2 DIV 8 + 4 * (reg1 DIV 8)) END; OutByte2(28H * ORD(cmd.opcode = CODE.opDECB), reg2 MOD 8 + 8 * (reg1 MOD 8)); // add/sub byte[reg2], reg1_8 drop; drop |CODE.opSAVEIP: UnOp(reg1); reg2 := REG.GetAnyReg(R); lea(reg2, param2, sIMP); movrm(reg2, reg2, 0); push(reg2); drop; IF reg1 >= 8 THEN OutByte(41H) END; OutByte2(8FH, reg1 MOD 8); // pop qword[reg1] drop |CODE.opCLEANUP: n := param2 * 8; IF n # 0 THEN addrc(rsp, n) END |CODE.opPOPSP: pop(rsp) |CODE.opLOADF: UnOp(reg1); INC(xmm); movsdrm(xmm, reg1, 0); drop |CODE.opPUSHF: subrc(rsp, 8); movsdmr(rsp, 0, xmm); DEC(xmm) |CODE.opCONSTF: float := cmd.float; INC(xmm); reg1 := REG.GetAnyReg(R); lea(reg1, Numbers_Offs + Numbers_Count * 8, sDATA); movsdrm(xmm, reg1, 0); drop; NewNumber(UTILS.splitf(float, a, b)) |CODE.opSAVEF: UnOp(reg1); movsdmr(reg1, 0, xmm); DEC(xmm); drop |CODE.opADDF, CODE.opADDFI: opxx(58H, xmm - 1, xmm); DEC(xmm) |CODE.opSUBF: opxx(5CH, xmm - 1, xmm); DEC(xmm) |CODE.opSUBFI: opxx(5CH, xmm, xmm - 1); opxx(10H, xmm - 1, xmm); DEC(xmm) |CODE.opMULF: opxx(59H, xmm - 1, xmm); DEC(xmm) |CODE.opDIVF: opxx(5EH, xmm - 1, xmm); DEC(xmm) |CODE.opDIVFI: opxx(5EH, xmm, xmm - 1); opxx(10H, xmm - 1, xmm); DEC(xmm) |CODE.opUMINF: reg1 := REG.GetAnyReg(R); lea(reg1, Numbers_Offs, sDATA); OutByte3(66H, 40H + reg1 DIV 8 + (xmm DIV 8) * 4, 0FH); // xorpd xmm, xmmword[reg1] OutByte2(57H, reg1 MOD 8 + (xmm MOD 8) * 8); drop |CODE.opFABS: reg1 := REG.GetAnyReg(R); lea(reg1, Numbers_Offs + 16, sDATA); OutByte3(66H, 40H + reg1 DIV 8 + (xmm DIV 8) * 4, 0FH); // andpd xmm, xmmword[reg1] OutByte2(54H, reg1 MOD 8 + (xmm MOD 8) * 8); drop |CODE.opFLT: UnOp(reg1); INC(xmm); OutByte(0F2H); Rex(reg1, xmm); OutByte(0FH); // cvtsi2sd xmm, reg1 OutByte2(2AH, 0C0H + (xmm MOD 8) * 8 + reg1 MOD 8); drop |CODE.opFLOOR: reg1 := REG.GetAnyReg(R); subrc(rsp, 8); OutByte3(00FH, 0AEH, 05CH); OutByte2(024H, 004H); // stmxcsr dword[rsp+4]; OutByte2(00FH, 0AEH); OutByte2(01CH, 024H); // stmxcsr dword[rsp]; OutByte3(081H, 024H, 024H); OutByte2(0FFH, 09FH); OutByte2(0FFH, 0FFH); // and dword[rsp],11111111111111111001111111111111b; OutByte3(081H, 00CH, 024H); OutByte2(000H, 020H); OutByte2(000H, 000H); // or dword[rsp],00000000000000000010000000000000b; OutByte2(00FH, 0AEH); OutByte2(014H, 024H); // ldmxcsr dword[rsp]; OutByte(0F2H); Rex(xmm, reg1); OutByte(0FH); // cvtsd2si reg1, xmm OutByte2(2DH, 0C0H + xmm MOD 8 + (reg1 MOD 8) * 8); OutByte3(00FH, 0AEH, 054H); OutByte2(024H, 004H); // ldmxcsr dword[rsp+4]; addrc(rsp, 8); DEC(xmm) |CODE.opEQF .. CODE.opGEFI: fcmp(cmd.opcode, xmm); DEC(xmm, 2) |CODE.opINF: INC(xmm); reg1 := REG.GetAnyReg(R); lea(reg1, Numbers_Offs + 32, sDATA); movsdrm(xmm, reg1, 0); drop |CODE.opPACK, CODE.opPACKC: IF cmd.opcode = CODE.opPACK THEN BinOp(reg1, reg2) ELSE UnOp(reg1); reg2 := REG.GetAnyReg(R); movrc(reg2, param2) END; push(reg1); movrm(reg1, reg1, 0); shiftrc(shl, reg1, 1); shiftrc(shr, reg1, 53); add(reg1, reg2); andrc(reg1, ORD({0..10})); shiftrc(shl, reg1, 52); movrm(reg2, rsp, 0); movrm(reg2, reg2, 0); push(reg1); lea(reg1, Numbers_Offs + 40, sDATA); // {0..51, 63} movrm(reg1, reg1, 0); and(reg2, reg1); pop(reg1); or(reg2, reg1); pop(reg1); movmr(reg1, 0, reg2); drop; drop |CODE.opUNPK, CODE.opLADR_UNPK: IF cmd.opcode = CODE.opLADR_UNPK THEN n := param2 * 8; UnOp(reg1); reg2 := REG.GetVarReg(R, param2); regVar := reg2 # -1; IF ~regVar THEN reg2 := REG.GetAnyReg(R); Rex(0, reg2); OutByte2(8DH, 45H + long(n) + (reg2 MOD 8) * 8); // lea reg2, qword[rbp+n] OutIntByte(n) END ELSE BinOp(reg1, reg2); regVar := FALSE END; push(reg1); movrm(reg1, reg1, 0); shiftrc(shl, reg1, 1); shiftrc(shr, reg1, 53); subrc(reg1, 1023); IF regVar THEN mov(reg2, reg1); reg2 := REG.GetAnyReg(R) ELSE movmr(reg2, 0, reg1) END; pop(reg2); movrm(reg1, reg2, 0); push(reg2); lea(reg2, Numbers_Offs + 48, sDATA); // {52..61} movrm(reg2, reg2, 0); or(reg1, reg2); pop(reg2); Rex(reg1, 0); OutByte2(0FH, 0BAH); OutByte2(0F0H + reg1 MOD 8, 3EH); // btr reg1, 62 movmr(reg2, 0, reg1); drop; drop |CODE.opSADR_PARAM: pushDA(stroffs + param2) |CODE.opVADR_PARAM: pushm(rbp, param2 * 8) |CODE.opLOAD64_PARAM: UnOp(reg1); pushm(reg1, 0); drop |CODE.opLLOAD64_PARAM: reg1 := REG.GetVarReg(R, param2); IF reg1 # -1 THEN push(reg1) ELSE pushm(rbp, param2 * 8) END |CODE.opGLOAD64_PARAM: reg2 := REG.GetAnyReg(R); lea(reg2, param2, sBSS); movrm(reg2, reg2, 0); push(reg2); drop |CODE.opCONST_PARAM: pushc(param2) |CODE.opGLOAD32_PARAM: reg1 := REG.GetAnyReg(R); xor(reg1, reg1); lea(reg1, param2, sBSS); movrm32(reg1, reg1, 0); push(reg1); drop |CODE.opLOAD32_PARAM: UnOp(reg1); movrm32(reg1, reg1, 0); shiftrc(shl, reg1, 32); shiftrc(shr, reg1, 32); push(reg1); drop |CODE.opLLOAD32_PARAM: reg1 := REG.GetAnyReg(R); xor(reg1, reg1); reg2 := REG.GetVarReg(R, param2); IF reg2 # -1 THEN mov(reg1, reg2) ELSE movrm32(reg1, rbp, param2 * 8) END; push(reg1); drop |CODE.opLADR_SAVEC: n := param1 * 8; reg1 := REG.GetVarReg(R, param1); IF reg1 # -1 THEN movrc(reg1, param2) ELSE IF isLong(param2) THEN reg2 := REG.GetAnyReg(R); movrc(reg2, param2); movmr(rbp, n, reg2); drop ELSE OutByte3(48H, 0C7H, 45H + long(n)); // mov qword[rbp+n],param2 OutIntByte(n); OutInt(param2) END END |CODE.opGADR_SAVEC: IF isLong(param2) THEN reg1 := REG.GetAnyReg(R); movrc(reg1, param2); reg2 := REG.GetAnyReg(R); lea(reg2, param1, sBSS); movmr(reg2, 0, reg1); drop; drop ELSE reg2 := REG.GetAnyReg(R); lea(reg2, param1, sBSS); Rex(reg2, 0); OutByte2(0C7H, reg2 MOD 8); // mov qword[reg2], param2 OutInt(param2); drop END |CODE.opLADR_SAVE: UnOp(reg1); reg2 := REG.GetVarReg(R, param2); IF reg2 # -1 THEN mov(reg2, reg1) ELSE movmr(rbp, param2 * 8, reg1) END; drop |CODE.opLADR_INC1: reg1 := REG.GetVarReg(R, param2); IF reg1 # -1 THEN incr(reg1) ELSE n := param2 * 8; OutByte3(48H, 0FFH, 45H + long(n)); // inc qword[rbp+n] OutIntByte(n) END |CODE.opLADR_DEC1: reg1 := REG.GetVarReg(R, param2); IF reg1 # -1 THEN decr(reg1) ELSE n := param2 * 8; OutByte3(48H, 0FFH, 4DH + long(n)); // dec qword[rbp+n] OutIntByte(n) END |CODE.opLADR_INCC, CODE.opLADR_DECC: reg1 := REG.GetVarReg(R, param1); IF isLong(param2) THEN reg2 := REG.GetAnyReg(R); movrc(reg2, param2); IF reg1 # -1 THEN IF cmd.opcode = CODE.opLADR_DECC THEN sub(reg1, reg2) ELSE add(reg1, reg2) END ELSE n := param1 * 8; Rex(0, reg2); OutByte2(01H + 28H * ORD(cmd.opcode = CODE.opLADR_DECC), 45H + long(n) + (reg2 MOD 8) * 8); OutIntByte(n) // add/sub qword[rbp+n],reg2 END; drop ELSE IF reg1 # -1 THEN IF cmd.opcode = CODE.opLADR_DECC THEN subrc(reg1, param2) ELSE addrc(reg1, param2) END ELSE n := param1 * 8; OutByte3(48H, 81H + short(param2), 45H + long(n) + 28H * ORD(cmd.opcode = CODE.opLADR_DECC)); OutIntByte(n); OutIntByte(param2) // add/sub qword[rbp+n],param2 END END |CODE.opLADR_INC1B, CODE.opLADR_DEC1B: reg1 := REG.GetVarReg(R, param2); IF reg1 # -1 THEN IF cmd.opcode = CODE.opLADR_DEC1B THEN decr(reg1) ELSE incr(reg1) END; andrc(reg1, 255) ELSE n := param2 * 8; OutByte2(0FEH, 45H + long(n) + 8 * ORD(cmd.opcode = CODE.opLADR_DEC1B)); OutIntByte(n) // inc/dec byte[rbp+n] END |CODE.opLADR_INCCB, CODE.opLADR_DECCB: reg1 := REG.GetVarReg(R, param1); param2 := param2 MOD 256; IF reg1 # -1 THEN IF cmd.opcode = CODE.opLADR_DECCB THEN subrc(reg1, param2) ELSE addrc(reg1, param2) END; andrc(reg1, 255) ELSE n := param1 * 8; OutByte2(80H, 45H + long(n) + 28H * ORD(cmd.opcode = CODE.opLADR_DECCB)); OutIntByte(n); OutByte(param2) // add/sub byte[rbp+n],param2 END |CODE.opLADR_INC, CODE.opLADR_DEC: UnOp(reg1); reg2 := REG.GetVarReg(R, param2); IF reg2 # -1 THEN IF cmd.opcode = CODE.opLADR_DEC THEN sub(reg2, reg1) ELSE add(reg2, reg1) END ELSE n := param2 * 8; Rex(0, reg1); OutByte2(01H + 28H * ORD(cmd.opcode = CODE.opLADR_DEC), 45H + long(n) + (reg1 MOD 8) * 8); OutIntByte(n) // add/sub qword[rbp+n],reg1 END; drop |CODE.opLADR_INCB, CODE.opLADR_DECB: UnOp(reg1); reg2 := REG.GetVarReg(R, param2); IF reg2 # -1 THEN IF cmd.opcode = CODE.opLADR_DECB THEN sub(reg2, reg1) ELSE add(reg2, reg1) END; andrc(reg2, 255) ELSE n := param2 * 8; IF reg1 >= 8 THEN OutByte(44H) END; OutByte2(28H * ORD(cmd.opcode = CODE.opLADR_DECB), 45H + long(n) + 8 * (reg1 MOD 8)); OutIntByte(n) // add/sub byte[rbp+n], reg1_8 END; drop |CODE.opLADR_INCL, CODE.opLADR_EXCL: UnOp(reg1); cmprc(reg1, 64); reg2 := REG.GetVarReg(R, param2); IF reg2 # -1 THEN OutByte2(73H, 4); // jnb L oprr2(0FH, 0ABH + 8 * ORD(cmd.opcode = CODE.opLADR_EXCL), reg2, reg1) // bts/btr reg2, reg1 ELSE n := param2 * 8; OutByte2(73H, 5 + 3 * ORD(~isByte(n))); // jnb L Rex(0, reg1); OutByte3(0FH, 0ABH + 8 * ORD(cmd.opcode = CODE.opLADR_EXCL), 45H + long(n) + 8 * (reg1 MOD 8)); OutIntByte(n) // bts/btr qword[rbp+n], reg1 END; // L: drop |CODE.opLADR_INCLC, CODE.opLADR_EXCLC: reg1 := REG.GetVarReg(R, param1); IF reg1 # -1 THEN Rex(reg1, 0); OutByte3(0FH, 0BAH, 0E8H); // bts/btr reg1, param2 OutByte2(reg1 MOD 8 + 8 * ORD(cmd.opcode = CODE.opLADR_EXCLC), param2) ELSE n := param1 * 8; OutByte3(48H, 0FH, 0BAH); // bts/btr qword[rbp+n], param2 OutByte(6DH + long(n) + 8 * ORD(cmd.opcode = CODE.opLADR_EXCLC)); OutIntByte(n); OutByte(param2) END |CODE.opLOOP, CODE.opENDLOOP: END; cmd := cmd.next(COMMAND) END; ASSERT(R.pushed = 0); ASSERT(R.top = -1); ASSERT(xmm = -1) END translate; PROCEDURE prolog (code: CODE.CODES; modname: ARRAY OF CHAR; target, stack_size: INTEGER); VAR ModName_Offs, entry: INTEGER; BEGIN ModName_Offs := CHL.Length(code.types) * 8 + CHL.Length(code.data); Numbers_Offs := ModName_Offs + LENGTH(modname) + 1; ASSERT(MACHINE.Align(Numbers_Offs, 16)); entry := NewLabel(); X86.SetLabel(entry); IF target = mConst.Target_iDLL64 THEN dllret := NewLabel(); push(r8); push(rdx); push(rcx); CallRTL(CODE._dllentry); test(rax); jcc(je, dllret) END; push(rsp); lea(rax, entry, sCODE); push(rax); pushDA(0); //TYPES pushc(CHL.Length(code.types)); pushDA(ModName_Offs); //MODNAME CallRTL(CODE._init) END prolog; PROCEDURE epilog (code: CODE.CODES; modname: ARRAY OF CHAR; target: INTEGER); VAR i, n: INTEGER; number: Number; exp: CODE.EXPORT_PROC; PROCEDURE import (imp: LISTS.LIST); VAR lib: CODE.IMPORT_LIB; proc: CODE.IMPORT_PROC; BEGIN lib := imp.first(CODE.IMPORT_LIB); WHILE lib # NIL DO BIN.Import(prog, lib.name, 0); proc := lib.procs.first(CODE.IMPORT_PROC); WHILE proc # NIL DO BIN.Import(prog, proc.name, proc.label); proc := proc.next(CODE.IMPORT_PROC) END; lib := lib.next(CODE.IMPORT_LIB) END END import; BEGIN IF target = mConst.Target_iDLL64 THEN X86.SetLabel(dllret); OutByte(0C3H) // ret ELSE pushc(0); CallRTL(CODE._exit) END; X86.fixup; i := 0; WHILE i < CHL.Length(code.types) DO BIN.PutData64LE(prog, CHL.GetInt(code.types, i)); INC(i) END; i := 0; WHILE i < CHL.Length(code.data) DO BIN.PutData(prog, CHL.GetByte(code.data, i)); INC(i) END; BIN.PutDataStr(prog, modname); BIN.PutData(prog, 0); n := CHL.Length(prog.data); ASSERT(MACHINE.Align(n, 16)); i := n - CHL.Length(prog.data); WHILE i > 0 DO BIN.PutData(prog, 0); DEC(i) END; number := Numbers.first(Number); FOR i := 0 TO Numbers_Count - 1 DO BIN.PutData64LE(prog, number.value); number := number.next(Number) END; exp := code.export.first(CODE.EXPORT_PROC); WHILE exp # NIL DO BIN.Export(prog, exp.name, exp.label); exp := exp.next(CODE.EXPORT_PROC) END; import(code.import) END epilog; PROCEDURE rload (reg, offs, size: INTEGER); BEGIN offs := offs * 8; CASE size OF |1: movzx(reg, rbp, offs, FALSE) |2: movzx(reg, rbp, offs, TRUE) |4: xor(reg, reg); movrm32(reg, rbp, offs) |8: movrm(reg, rbp, offs) END END rload; PROCEDURE rsave (reg, offs, size: INTEGER); BEGIN offs := offs * 8; CASE size OF |1: movmr8(rbp, offs, reg) |2: movmr16(rbp, offs, reg) |4: movmr32(rbp, offs, reg) |8: movmr(rbp, offs, reg) END END rsave; PROCEDURE CodeGen* (code: CODE.CODES; outname: ARRAY OF CHAR; target, stack, base: INTEGER); VAR path, modname, ext: PATHS.PATH; n: INTEGER; BEGIN Win64RegPar[0] := rcx; Win64RegPar[1] := rdx; Win64RegPar[2] := r8; Win64RegPar[3] := r9; SystemVRegPar[0] := rdi; SystemVRegPar[1] := rsi; SystemVRegPar[2] := rdx; SystemVRegPar[3] := rcx; SystemVRegPar[4] := r8; SystemVRegPar[5] := r9; PATHS.split(outname, path, modname, ext); S.append(modname, ext); R := REG.Create(push, pop, mov, xchg, rload, rsave, {rax, r10, r11}, {rcx, rdx, r8, r9}); n := code.dmin - CHL.Length(code.data); IF n > 0 THEN INC(code.bss, n) END; code.bss := MAX(code.bss, 8); Numbers := LISTS.create(NIL); Numbers_Count := 0; NewNumber(ROR(1, 1)); (* 8000000000000000H *) NewNumber(0); NewNumber(ROR(-2, 1)); (* 7FFFFFFFFFFFFFFFH *) NewNumber(-1); NewNumber(ROR(7FFH, 12)); (* +Infinity *) NewNumber(ORD(-BITS(LSR(ASR(ROR(1, 1), 10), 1)))); (* {0..51, 63} *) NewNumber(LSR(ASR(ROR(1, 1), 9), 2)); (* {52..61} *) prog := BIN.create(code.lcount); BIN.SetParams(prog, code.bss, stack, WCHR(1), WCHR(0)); X86.SetProgram(prog); prolog(code, modname, target, stack); translate(code.commands, CHL.Length(code.types) * 8); epilog(code, modname, target); BIN.fixup(prog); IF target IN {mConst.Target_iConsole64, mConst.Target_iGUI64, mConst.Target_iDLL64} THEN PE32.write(prog, outname, base, target = mConst.Target_iConsole64, target = mConst.Target_iDLL64, TRUE) ELSIF target = mConst.Target_iELF64 THEN ELF.write(prog, outname, TRUE) END END CodeGen; END AMD64.