(* 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; param2: 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, CODE.opSAVES, CODE.opRSET, CODE.opRSETR, CODE.opRSETL, CODE.opRSET1, CODE.opEQS .. CODE.opGES, CODE.opEQS2 .. CODE.opGES2, CODE.opEQSW .. CODE.opGESW, CODE.opEQSW2 .. CODE.opGESW2, CODE.opCOPY, CODE.opMOVE, CODE.opCOPYA, CODE.opCOPYS, CODE.opCOPYS2, CODE.opROT, CODE.opNEW, CODE.opDISP, CODE.opISREC, CODE.opIS, CODE.opTYPEGR, CODE.opTYPEGP, CODE.opCASET, CODE.opDIV, CODE.opDIVL, CODE.opMOD, CODE.opMODL, CODE.opLENGTH, CODE.opLENGTHW: leaf := FALSE |CODE.opDIVR, CODE.opMODR: param2 := cur.param2; IF param2 >= 1 THEN param2 := X86.log2(param2) ELSIF param2 <= -1 THEN param2 := X86.log2(-param2) ELSE param2 := -1 END; IF param2 < 0 THEN leaf := FALSE END 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.opACC: IF (R.top # 0) OR (R.stk[0] # rax) THEN PushAll(0); GetRegA; pop(rax); DEC(R.pushed) 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.