(* BSD 2-Clause License Copyright (c) 2018-2019, Anton Krotov All rights reserved. *) MODULE AMD64; IMPORT IL, BIN, WR := WRITER, CHL := CHUNKLISTS, LISTS, PATHS, PROG, 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 = IL.opLSL2; shr = IL.opLSR2; sar = IL.opASR2; ror = IL.opROR2; sCODE = BIN.PICCODE; sDATA = BIN.PICDATA; sBSS = BIN.PICBSS; sIMP = BIN.PICIMP; TYPE COMMAND = IL.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; tcount: INTEGER; dllret, sofinit: 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(UTILS.Byte(n, 0)); OutByte(UTILS.Byte(n, 1)); OutByte(UTILS.Byte(n, 2)); OutByte(UTILS.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(UTILS.Byte(n, 0)) ELSE OutInt(n) END END OutIntByte; PROCEDURE isLong (n: INTEGER): BOOLEAN; RETURN (n > UTILS.max32) OR (n < UTILS.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 IL.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 GetAnyReg (): INTEGER; RETURN REG.GetAnyReg(R) END GetAnyReg; PROCEDURE GetVarReg (offs: INTEGER): INTEGER; RETURN REG.GetVarReg(R, offs) END GetVarReg; PROCEDURE callimp (label: INTEGER); VAR reg: INTEGER; BEGIN reg := GetAnyReg(); 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 := GetAnyReg(); lea(reg, offs, sDATA); push(reg); drop END pushDA; PROCEDURE CallRTL (proc: INTEGER); VAR label: INTEGER; BEGIN REG.Store(R); label := IL.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); DEC(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(UTILS.Byte(n, i)) END END movabs; PROCEDURE movrc (reg, n: INTEGER); // mov reg, n BEGIN IF isLong(n) THEN movabs(reg, n) ELSIF n = 0 THEN xor(reg, reg) 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 := GetAnyReg(); 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 := GetAnyReg(); 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): IL.LOCALVAR; VAR cur: IL.LOCALVAR; BEGIN cur := variables.first(IL.LOCALVAR); WHILE (cur # NIL) & (cur.offset # offset) DO cur := cur.next(IL.LOCALVAR) END RETURN cur END getVar; PROCEDURE allocReg (cmd: COMMAND); VAR leave: BOOLEAN; leaf: BOOLEAN; cur: COMMAND; variables: LISTS.LIST; lvar, rvar: IL.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 |IL.opLLOAD64, IL.opLLOAD8, IL.opLLOAD16, IL.opLLOAD32, IL.opLLOAD64_PARAM, IL.opLLOAD32_PARAM, IL.opLADR_SAVE, IL.opLADR_INC, IL.opLADR_DEC, IL.opLADR_INCB, IL.opLADR_DECB, IL.opLADR_INCL, IL.opLADR_EXCL, IL.opLADR_UNPK: lvar := getVar(variables, cur.param2); IF (lvar # NIL) & (lvar.count # -1) THEN INC(lvar.count, loop) END |IL.opLADR_SAVEC, IL.opLADR_INCC, IL.opLADR_INCCB, IL.opLADR_DECCB, IL.opLADR_INCLC, IL.opLADR_EXCLC: lvar := getVar(variables, cur.param1); IF (lvar # NIL) & (lvar.count # -1) THEN INC(lvar.count, loop) END |IL.opLADR: lvar := getVar(variables, cur.param2); IF (lvar # NIL) & (lvar.count # -1) THEN lvar.count := -1 END |IL.opLOOP: INC(loop, 10) |IL.opENDLOOP: DEC(loop, 10) |IL.opLEAVE, IL.opLEAVER, IL.opLEAVEF: leave := TRUE |IL.opCALL, IL.opCALLP, IL.opCALLI, IL.opWIN64CALL, IL.opWIN64CALLP, IL.opWIN64CALLI, IL.opSYSVCALL, IL.opSYSVCALLP, IL.opSYSVCALLI, IL.opSAVES, IL.opRSET, IL.opRSETR, IL.opRSETL, IL.opRSET1, IL.opEQS .. IL.opGES, IL.opEQSW .. IL.opGESW, IL.opCOPY, IL.opMOVE, IL.opCOPYA, IL.opCOPYS, IL.opROT, IL.opNEW, IL.opDISP, IL.opISREC, IL.opIS, IL.opTYPEGR, IL.opTYPEGP, IL.opTYPEGD, IL.opCASET, IL.opDIV, IL.opDIVL, IL.opMOD, IL.opMODL, IL.opLENGTH, IL.opLENGTHW: leaf := FALSE |IL.opDIVR, IL.opMODR: param2 := cur.param2; IF param2 >= 1 THEN param2 := UTILS.Log2(param2) ELSIF param2 <= -1 THEN param2 := UTILS.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(IL.LOCALVAR); WHILE lvar # NIL DO IF lvar.count > max THEN max := lvar.count; rvar := lvar END; lvar := lvar.next(IL.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 := GetAnyReg(); xor(reg, reg); CASE op OF |IL.opEQF: comisd(xmm - 1, xmm); cc := sete |IL.opNEF: comisd(xmm - 1, xmm); cc := setne |IL.opLTF: comisd(xmm - 1, xmm); cc := setc |IL.opGTF: comisd(xmm, xmm - 1); cc := setc |IL.opLEF: comisd(xmm, xmm - 1); cc := setnc |IL.opGEF: 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; opcode, 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; opcode := cmd.opcode; CASE opcode OF |IL.opJMP: jmp(param1) |IL.opCALL, IL.opWIN64CALL, IL.opSYSVCALL: REG.Store(R); CASE opcode OF |IL.opCALL: |IL.opWIN64CALL: Win64Passing(param2) |IL.opSYSVCALL: SysVPassing(param2) END; X86.call(param1); REG.Restore(R) |IL.opCALLP, IL.opWIN64CALLP, IL.opSYSVCALLP: UnOp(reg1); IF reg1 # rax THEN GetRegA; ASSERT(REG.Exchange(R, reg1, rax)); drop END; drop; REG.Store(R); CASE opcode OF |IL.opCALLP: |IL.opWIN64CALLP: Win64Passing(param2) |IL.opSYSVCALLP: SysVPassing(param2) END; OutByte2(0FFH, 0D0H); // call rax REG.Restore(R); ASSERT(R.top = -1) |IL.opCALLI, IL.opWIN64CALLI, IL.opSYSVCALLI: REG.Store(R); CASE opcode OF |IL.opCALLI: |IL.opWIN64CALLI: Win64Passing(param2) |IL.opSYSVCALLI: SysVPassing(param2) END; callimp(param1); REG.Restore(R) |IL.opLABEL: X86.SetLabel(param1) |IL.opERR: CallRTL(IL._error) |IL.opPUSHC: pushc(param2) |IL.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) |IL.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) |IL.opSYSVALIGN16: ASSERT(rax IN R.regs); mov(rax, rsp); andrc(rsp, -16); push(rax); IF ~ODD(param2) THEN push(rax) END |IL.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 |IL.opRES: ASSERT(R.top = -1); GetRegA; n := param2; WHILE n > 0 DO INC(xmm); movsdrm(xmm, rsp, 0); addrc(rsp, 8); DEC(n) END |IL.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 |IL.opLEAVE, IL.opLEAVER, IL.opLEAVEF: IF opcode = IL.opLEAVER THEN UnOp(reg1); IF reg1 # rax THEN GetRegA; ASSERT(REG.Exchange(R, reg1, rax)); drop END; drop END; ASSERT(R.top = -1); IF opcode = IL.opLEAVEF THEN DEC(xmm) END; ASSERT(xmm = -1); IF param1 > 0 THEN mov(rsp, rbp) END; 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) |IL.opSAVES: UnOp(reg1); REG.PushAll_1(R); pushDA(stroffs + param2); push(reg1); drop; pushc(param1); CallRTL(IL._move) |IL.opSADR: lea(GetAnyReg(), stroffs + param2, sDATA) |IL.opLOAD8: UnOp(reg1); movzx(reg1, reg1, 0, FALSE) |IL.opLOAD16: UnOp(reg1); movzx(reg1, reg1, 0, TRUE) |IL.opLOAD32: UnOp(reg1); movrm32(reg1, reg1, 0); shiftrc(shl, reg1, 32); shiftrc(shr, reg1, 32) |IL.opLOAD64: UnOp(reg1); movrm(reg1, reg1, 0) |IL.opLLOAD64: reg1 := GetAnyReg(); reg2 := GetVarReg(param2); IF reg2 # -1 THEN mov(reg1, reg2) ELSE movrm(reg1, rbp, param2 * 8) END |IL.opLLOAD8, IL.opLLOAD16: reg1 := GetAnyReg(); reg2 := GetVarReg(param2); IF reg2 # -1 THEN mov(reg1, reg2) ELSE movzx(reg1, rbp, param2 * 8, opcode = IL.opLLOAD16) END |IL.opLLOAD32: reg1 := GetAnyReg(); reg2 := GetVarReg(param2); IF reg2 # -1 THEN mov(reg1, reg2) ELSE n := param2 * 8; xor(reg1, reg1); movrm32(reg1, rbp, n) END |IL.opGLOAD64: reg1 := GetAnyReg(); lea(reg1, param2, sBSS); movrm(reg1, reg1, 0) |IL.opGLOAD8: reg1 := GetAnyReg(); lea(reg1, param2, sBSS); movzx(reg1, reg1, 0, FALSE) |IL.opGLOAD16: reg1 := GetAnyReg(); lea(reg1, param2, sBSS); movzx(reg1, reg1, 0, TRUE) |IL.opGLOAD32: reg1 := GetAnyReg(); xor(reg1, reg1); lea(reg1, param2, sBSS); movrm32(reg1, reg1, 0) |IL.opVLOAD64: reg1 := GetAnyReg(); movrm(reg1, rbp, param2 * 8); movrm(reg1, reg1, 0) |IL.opVLOAD8, IL.opVLOAD16: reg1 := GetAnyReg(); movrm(reg1, rbp, param2 * 8); movzx(reg1, reg1, 0, opcode = IL.opVLOAD16) |IL.opVLOAD32: reg1 := GetAnyReg(); reg2 := GetAnyReg(); xor(reg1, reg1); movrm(reg2, rbp, param2 * 8); movrm32(reg1, reg2, 0); drop |IL.opLADR: n := param2 * 8; next := cmd.next(COMMAND); IF next.opcode = IL.opSAVEF THEN movsdmr(rbp, n, xmm); DEC(xmm); cmd := next ELSIF next.opcode = IL.opLOADF THEN INC(xmm); movsdrm(xmm, rbp, n); cmd := next ELSE reg1 := GetAnyReg(); Rex(0, reg1); OutByte2(8DH, 45H + long(n) + (reg1 MOD 8) * 8); // lea reg1, qword[rbp+n] OutIntByte(n) END |IL.opGADR: lea(GetAnyReg(), param2, sBSS) |IL.opVADR: movrm(GetAnyReg(), rbp, param2 * 8) |IL.opSAVE8C: UnOp(reg1); IF reg1 >= 8 THEN OutByte(41H) END; OutByte3(0C6H, reg1 MOD 8, param2); // mov byte[reg1], param2 drop |IL.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 |IL.opSAVEC: UnOp(reg1); IF isLong(param2) THEN reg2 := GetAnyReg(); 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 |IL.opRSET: PushAll(2); CallRTL(IL._set); GetRegA |IL.opRSETR: PushAll(1); pushc(param2); CallRTL(IL._set); GetRegA |IL.opRSETL: UnOp(reg1); REG.PushAll_1(R); pushc(param2); push(reg1); drop; CallRTL(IL._set); GetRegA |IL.opRSET1: PushAll(1); CallRTL(IL._set1); GetRegA |IL.opINCL, IL.opEXCL: BinOp(reg1, reg2); cmprc(reg1, 64); OutByte2(73H, 04H); // jnb L Rex(reg2, reg1); OutByte3(0FH, 0ABH + 8 * ORD(opcode = IL.opEXCL), 8 * (reg1 MOD 8) + reg2 MOD 8); // bts/btr qword[reg2], reg1 // L: drop; drop |IL.opINCLC, IL.opEXCLC: UnOp(reg1); Rex(reg1, 0); OutByte2(0FH, 0BAH); // bts/btr qword[reg1], param2 OutByte2(28H + 8 * ORD(opcode = IL.opEXCLC) + reg1 MOD 8, param2); drop |IL.opEQS .. IL.opGES: PushAll(4); pushc(opcode - IL.opEQS); CallRTL(IL._strcmp); GetRegA |IL.opEQSW .. IL.opGESW: PushAll(4); pushc(opcode - IL.opEQSW); CallRTL(IL._strcmpw); GetRegA |IL.opCONST: movrc(GetAnyReg(), param2) |IL.opEQ..IL.opGE, IL.opEQC..IL.opGEC: IF (IL.opEQ <= opcode) & (opcode <= IL.opGE) THEN BinOp(reg1, reg2); cmprr(reg1, reg2); drop ELSE UnOp(reg1); IF param2 = 0 THEN test(reg1) ELSE cmprc(reg1, param2) END END; drop; cc := X86.cond(opcode); IF cmd.next(COMMAND).opcode = IL.opJE THEN label := cmd.next(COMMAND).param1; jcc(cc, label); cmd := cmd.next(COMMAND) ELSIF cmd.next(COMMAND).opcode = IL.opJNE THEN label := cmd.next(COMMAND).param1; jcc(X86.inv0(cc), label); cmd := cmd.next(COMMAND) ELSE reg1 := GetAnyReg(); setcc(cc + 16, reg1); andrc(reg1, 1) END |IL.opCODE: OutByte(param2) |IL.opPUSHIP: reg1 := GetAnyReg(); lea(reg1, param2, sIMP); movrm(reg1, reg1, 0) |IL.opPARAM: n := param2; IF n = 1 THEN UnOp(reg1); push(reg1); drop ELSE ASSERT(R.top + 1 <= n); PushAll(n) END |IL.opACC: IF (R.top # 0) OR (R.stk[0] # rax) THEN PushAll(0); GetRegA; pop(rax); DEC(R.pushed) END |IL.opJNZ: UnOp(reg1); test(reg1); jcc(jne, param1) |IL.opJZ: UnOp(reg1); test(reg1); jcc(je, param1) |IL.opJE: UnOp(reg1); test(reg1); jcc(jne, param1); drop |IL.opJNE: UnOp(reg1); test(reg1); jcc(je, param1); drop |IL.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 |IL.opINR: label := NewLabel(); L := NewLabel(); UnOp(reg1); reg2 := GetAnyReg(); 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 |IL.opINL: UnOp(reg1); Rex(reg1, 0); OutByte2(0FH, 0BAH); // bt reg1, param2 OutByte2(0E0H + reg1 MOD 8, param2); setcc(setc, reg1); andrc(reg1, 1) |IL.opNOT: UnOp(reg1); test(reg1); setcc(sete, reg1); andrc(reg1, 1) |IL.opORD: UnOp(reg1); test(reg1); setcc(setne, reg1); andrc(reg1, 1) |IL.opABS: UnOp(reg1); test(reg1); OutByte2(7DH, 03H); // jge L neg(reg1) // L: |IL.opEQB, IL.opNEB: BinOp(reg1, reg2); drop; test(reg1); label := NewLabel(); jcc(je, label); movrc(reg1, 1); X86.SetLabel(label); test(reg2); label := NewLabel(); jcc(je, label); movrc(reg2, 1); X86.SetLabel(label); cmprr(reg1, reg2); IF opcode = IL.opEQB THEN setcc(sete, reg1) ELSE setcc(setne, reg1) END; andrc(reg1, 1) |IL.opMULSC: UnOp(reg1); andrc(reg1, param2) |IL.opDIVSC, IL.opADDSL, IL.opADDSR: UnOp(reg1); Rex(reg1, 0); OutByte2(81H + short(param2), 0C8H + 28H * ORD(opcode = IL.opDIVSC) + reg1 MOD 8); // or/xor reg1, param2 OutIntByte(param2) |IL.opSUBSL: UnOp(reg1); not(reg1); andrc(reg1, param2) |IL.opSUBSR: UnOp(reg1); andrc(reg1, ORD(-BITS(param2))) |IL.opMULS: BinOp(reg1, reg2); and(reg1, reg2); drop |IL.opDIVS: BinOp(reg1, reg2); xor(reg1, reg2); drop |IL.opUMINS: UnOp(reg1); not(reg1) |IL.opCOPY: PushAll(2); pushc(param2); CallRTL(IL._move) |IL.opMOVE: PushAll(3); CallRTL(IL._move) |IL.opCOPYA: PushAll(4); pushc(param2); CallRTL(IL._arrcpy); GetRegA |IL.opCOPYS: PushAll(4); pushc(param2); CallRTL(IL._strcpy) |IL.opROT: PushAll(0); push(rsp); pushc(param2); CallRTL(IL._rot) |IL.opNEW: PushAll(1); n := param2 + 16; ASSERT(UTILS.Align(n, 64)); pushc(n); pushc(param1); CallRTL(IL._new) |IL.opDISP: PushAll(1); CallRTL(IL._dispose) |IL.opPUSHT: UnOp(reg1); reg2 := GetAnyReg(); movrm(reg2, reg1, -8) |IL.opISREC: PushAll(2); pushc(param2 * tcount); CallRTL(IL._isrec); GetRegA |IL.opIS: PushAll(1); pushc(param2 * tcount); CallRTL(IL._is); GetRegA |IL.opTYPEGR: PushAll(1); pushc(param2 * tcount); CallRTL(IL._guardrec); GetRegA |IL.opTYPEGP: UnOp(reg1); PushAll(0); push(reg1); pushc(param2 * tcount); CallRTL(IL._guard); GetRegA |IL.opTYPEGD: UnOp(reg1); PushAll(0); pushm(reg1, -8); pushc(param2 * tcount); CallRTL(IL._guardrec); GetRegA |IL.opCASET: push(r10); push(r10); pushc(param2 * tcount); CallRTL(IL._guardrec); pop(r10); test(rax); jcc(jne, param1) |IL.opSAVEP: UnOp(reg1); reg2 := GetAnyReg(); lea(reg2, param2, sCODE); movmr(reg1, 0, reg2); drop; drop |IL.opPUSHP: lea(GetAnyReg(), param2, sCODE) |IL.opINC, IL.opDEC: BinOp(reg1, reg2); // add/sub qword[reg2], reg1 Rex(reg2, reg1); OutByte2(01H + 28H * ORD(opcode = IL.opDEC), reg2 MOD 8 + (reg1 MOD 8) * 8); drop; drop |IL.opINCC: UnOp(reg1); IF isLong(param2) THEN reg2 := GetAnyReg(); movrc(reg2, param2); // add qword[reg1], reg2 Rex(reg1, reg2); OutByte2(01H, reg1 MOD 8 + (reg2 MOD 8) * 8); drop ELSIF ABS(param2) = 1 THEN Rex(reg1, 0); OutByte2(0FFH, reg1 MOD 8 + 8 * ORD(param2 = -1)) // inc/dec qword[reg1] ELSE // add qword[reg1], param2 Rex(reg1, 0); OutByte2(81H + short(param2), reg1 MOD 8); OutIntByte(param2) END; drop |IL.opDROP: UnOp(reg1); drop |IL.opSAVE, IL.opSAVE64: BinOp(reg2, reg1); movmr(reg1, 0, reg2); drop; drop |IL.opSAVE8: BinOp(reg2, reg1); movmr8(reg1, 0, reg2); drop; drop |IL.opSAVE16: BinOp(reg2, reg1); movmr16(reg1, 0, reg2); drop; drop |IL.opSAVE32: BinOp(reg2, reg1); movmr32(reg1, 0, reg2); drop; drop |IL.opMIN: BinOp(reg1, reg2); cmprr(reg1, reg2); OutByte2(7EH, 3); // jle L mov(reg1, reg2); // L: drop |IL.opMAX: BinOp(reg1, reg2); cmprr(reg1, reg2); OutByte2(7DH, 3); // jge L mov(reg1, reg2); // L: drop |IL.opMINC: UnOp(reg1); cmprc(reg1, param2); label := NewLabel(); jcc(jle, label); movrc(reg1, param2); X86.SetLabel(label) |IL.opMAXC: UnOp(reg1); cmprc(reg1, param2); label := NewLabel(); jcc(jge, label); movrc(reg1, param2); X86.SetLabel(label) |IL.opSBOOL: BinOp(reg2, reg1); test(reg2); IF reg1 >= 8 THEN OutByte(41H) END; OutByte3(0FH, 95H, reg1 MOD 8); // setne byte[reg1] drop; drop |IL.opSBOOLC: UnOp(reg1); IF reg1 >= 8 THEN OutByte(41H) END; OutByte3(0C6H, reg1 MOD 8, ORD(param2 # 0)); // mov byte[reg1], 0/1 drop |IL.opODD: UnOp(reg1); andrc(reg1, 1) |IL.opUMINUS: UnOp(reg1); neg(reg1) |IL.opADD: BinOp(reg1, reg2); add(reg1, reg2); drop |IL.opSUB: BinOp(reg1, reg2); sub(reg1, reg2); drop |IL.opSUBR, IL.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 opcode = IL.opSUBL THEN neg(reg1) END |IL.opADDL, IL.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 |IL.opDIV: PushAll(2); CallRTL(IL._divmod); GetRegA |IL.opDIVR: a := param2; IF a > 1 THEN n := UTILS.Log2(a) ELSIF a < -1 THEN n := UTILS.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 := GetAnyReg(); mov(reg2, reg1); shiftrc(sar, reg1, n); sub(reg1, reg2); drop ELSE shiftrc(sar, reg1, n) END ELSE PushAll(1); pushc(param2); CallRTL(IL._divmod); GetRegA END END |IL.opDIVL: UnOp(reg1); REG.PushAll_1(R); pushc(param2); push(reg1); drop; CallRTL(IL._divmod); GetRegA |IL.opMOD: PushAll(2); CallRTL(IL._divmod); mov(rax, rdx); GetRegA |IL.opMODR: a := param2; IF a > 1 THEN n := UTILS.Log2(a) ELSIF a < -1 THEN n := UTILS.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(IL._divmod); mov(rax, rdx); GetRegA END END |IL.opMODL: UnOp(reg1); REG.PushAll_1(R); pushc(param2); push(reg1); drop; CallRTL(IL._divmod); mov(rax, rdx); GetRegA |IL.opMUL: BinOp(reg1, reg2); oprr2(0FH, 0AFH, reg2, reg1); // imul reg1, reg2 drop |IL.opMULC: UnOp(reg1); a := param2; IF a > 1 THEN n := UTILS.Log2(a) ELSIF a < -1 THEN n := UTILS.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 |IL.opADDS: BinOp(reg1, reg2); or(reg1, reg2); drop |IL.opSUBS: BinOp(reg1, reg2); not(reg2); and(reg1, reg2); drop |IL.opNOP: |IL.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 |IL.opENDSW: |IL.opCASEL: cmprc(rax, param1); jcc(jl, param2) |IL.opCASER: cmprc(rax, param1); jcc(jg, param2) |IL.opCASELR: cmprc(rax, param1); jcc(jl, param2); jcc(jg, cmd.param3) |IL.opASR, IL.opROR, IL.opLSL, IL.opLSR: BinOp(reg1, reg2); xchg(reg2, rcx); Rex(reg1, 0); OutByte(0D3H); X86.shift(opcode, reg1 MOD 8); // shift reg1, cl xchg(reg2, rcx); drop |IL.opASR1, IL.opROR1, IL.opLSL1, IL.opLSR1: reg1 := GetAnyReg(); movrc(reg1, param2); BinOp(reg1, reg2); xchg(reg1, rcx); Rex(reg2, 0); OutByte(0D3H); X86.shift(opcode, reg2 MOD 8); // shift reg2, cl xchg(reg1, rcx); drop; drop; ASSERT(REG.GetReg(R, reg2)) |IL.opASR2, IL.opROR2, IL.opLSL2, IL.opLSR2: UnOp(reg1); shiftrc(opcode, reg1, param2 MOD 64) |IL.opGET, IL.opGETC: IF opcode = IL.opGET THEN BinOp(reg1, reg2) ELSIF opcode = IL.opGETC THEN UnOp(reg2); reg1 := GetAnyReg(); movrc(reg1, param1) END; drop; drop; _movrm(reg1, reg1, 0, param2 * 8, FALSE); _movrm(reg1, reg2, 0, param2 * 8, TRUE) |IL.opCHKBYTE: BinOp(reg1, reg2); cmprc(reg1, 256); jcc(jb, param1) |IL.opCHKIDX: UnOp(reg1); cmprc(reg1, param2); jcc(jb, param1) |IL.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 |IL.opLENGTH: PushAll(2); CallRTL(IL._length); GetRegA |IL.opLENGTHW: PushAll(2); CallRTL(IL._lengthw); GetRegA |IL.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)) |IL.opCHR: UnOp(reg1); andrc(reg1, 255) |IL.opWCHR: UnOp(reg1); andrc(reg1, 65535) |IL.opEQP, IL.opNEP, IL.opEQIP, IL.opNEIP: UnOp(reg1); reg2 := GetAnyReg(); CASE opcode OF |IL.opEQP, IL.opNEP: lea(reg2, param1, sCODE) |IL.opEQIP, IL.opNEIP: lea(reg2, param1, sIMP); movrm(reg2, reg2, 0) END; cmprr(reg1, reg2); drop; drop; reg1 := GetAnyReg(); CASE opcode OF |IL.opEQP, IL.opEQIP: setcc(sete, reg1) |IL.opNEP, IL.opNEIP: setcc(setne, reg1) END; andrc(reg1, 1) |IL.opINCCB, IL.opDECCB: UnOp(reg1); IF reg1 >= 8 THEN OutByte(41H) END; OutByte3(80H, 28H * ORD(opcode = IL.opDECCB) + reg1 MOD 8, param2 MOD 256); // add/sub byte[reg1], param2 MOD 256 drop |IL.opINCB, IL.opDECB: BinOp(reg1, reg2); IF (reg1 >= 8) OR (reg2 >= 8) THEN OutByte(40H + reg2 DIV 8 + 4 * (reg1 DIV 8)) END; OutByte2(28H * ORD(opcode = IL.opDECB), reg2 MOD 8 + 8 * (reg1 MOD 8)); // add/sub byte[reg2], reg1_8 drop; drop |IL.opSAVEIP: UnOp(reg1); reg2 := GetAnyReg(); 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 |IL.opCLEANUP: n := param2 * 8; IF n # 0 THEN addrc(rsp, n) END |IL.opPOPSP: pop(rsp) |IL.opLOADF: UnOp(reg1); INC(xmm); movsdrm(xmm, reg1, 0); drop |IL.opPUSHF: subrc(rsp, 8); movsdmr(rsp, 0, xmm); DEC(xmm) |IL.opCONSTF: float := cmd.float; INC(xmm); reg1 := GetAnyReg(); lea(reg1, Numbers_Offs + Numbers_Count * 8, sDATA); movsdrm(xmm, reg1, 0); drop; NewNumber(UTILS.splitf(float, a, b)) |IL.opSAVEF: UnOp(reg1); movsdmr(reg1, 0, xmm); DEC(xmm); drop |IL.opADDF, IL.opADDFI: opxx(58H, xmm - 1, xmm); DEC(xmm) |IL.opSUBF: opxx(5CH, xmm - 1, xmm); DEC(xmm) |IL.opSUBFI: opxx(5CH, xmm, xmm - 1); opxx(10H, xmm - 1, xmm); DEC(xmm) |IL.opMULF: opxx(59H, xmm - 1, xmm); DEC(xmm) |IL.opDIVF: opxx(5EH, xmm - 1, xmm); DEC(xmm) |IL.opDIVFI: opxx(5EH, xmm, xmm - 1); opxx(10H, xmm - 1, xmm); DEC(xmm) |IL.opUMINF: reg1 := GetAnyReg(); 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 |IL.opFABS: reg1 := GetAnyReg(); 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 |IL.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 |IL.opFLOOR: reg1 := GetAnyReg(); 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) |IL.opEQF .. IL.opGEF: fcmp(opcode, xmm); DEC(xmm, 2) |IL.opINF: INC(xmm); reg1 := GetAnyReg(); lea(reg1, Numbers_Offs + 32, sDATA); movsdrm(xmm, reg1, 0); drop |IL.opPACK, IL.opPACKC: IF opcode = IL.opPACK THEN BinOp(reg1, reg2) ELSE UnOp(reg1); reg2 := GetAnyReg(); 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 |IL.opUNPK, IL.opLADR_UNPK: IF opcode = IL.opLADR_UNPK THEN n := param2 * 8; UnOp(reg1); reg2 := GetVarReg(param2); regVar := reg2 # -1; IF ~regVar THEN reg2 := GetAnyReg(); 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 := GetAnyReg() 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 |IL.opSADR_PARAM: pushDA(stroffs + param2) |IL.opVADR_PARAM: pushm(rbp, param2 * 8) |IL.opLOAD64_PARAM: UnOp(reg1); pushm(reg1, 0); drop |IL.opLLOAD64_PARAM: reg1 := GetVarReg(param2); IF reg1 # -1 THEN push(reg1) ELSE pushm(rbp, param2 * 8) END |IL.opGLOAD64_PARAM: reg2 := GetAnyReg(); lea(reg2, param2, sBSS); movrm(reg2, reg2, 0); push(reg2); drop |IL.opCONST_PARAM: pushc(param2) |IL.opGLOAD32_PARAM: reg1 := GetAnyReg(); xor(reg1, reg1); lea(reg1, param2, sBSS); movrm32(reg1, reg1, 0); push(reg1); drop |IL.opLOAD32_PARAM: UnOp(reg1); movrm32(reg1, reg1, 0); shiftrc(shl, reg1, 32); shiftrc(shr, reg1, 32); push(reg1); drop |IL.opLLOAD32_PARAM: reg1 := GetAnyReg(); xor(reg1, reg1); reg2 := GetVarReg(param2); IF reg2 # -1 THEN mov(reg1, reg2) ELSE movrm32(reg1, rbp, param2 * 8) END; push(reg1); drop |IL.opLADR_SAVEC: n := param1 * 8; reg1 := GetVarReg(param1); IF reg1 # -1 THEN movrc(reg1, param2) ELSE IF isLong(param2) THEN reg2 := GetAnyReg(); 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 |IL.opGADR_SAVEC: IF isLong(param2) THEN reg1 := GetAnyReg(); movrc(reg1, param2); reg2 := GetAnyReg(); lea(reg2, param1, sBSS); movmr(reg2, 0, reg1); drop; drop ELSE reg2 := GetAnyReg(); lea(reg2, param1, sBSS); Rex(reg2, 0); OutByte2(0C7H, reg2 MOD 8); // mov qword[reg2], param2 OutInt(param2); drop END |IL.opLADR_SAVE: UnOp(reg1); reg2 := GetVarReg(param2); IF reg2 # -1 THEN mov(reg2, reg1) ELSE movmr(rbp, param2 * 8, reg1) END; drop |IL.opLADR_INCC: reg1 := GetVarReg(param1); IF isLong(param2) THEN reg2 := GetAnyReg(); movrc(reg2, param2); IF reg1 # -1 THEN add(reg1, reg2) ELSE n := param1 * 8; Rex(0, reg2); OutByte2(01H, 45H + long(n) + (reg2 MOD 8) * 8); OutIntByte(n) // add qword[rbp+n],reg2 END; drop ELSIF ABS(param2) = 1 THEN IF reg1 # -1 THEN IF param2 = 1 THEN incr(reg1) ELSE decr(reg1) END ELSE n := param1 * 8; OutByte3(48H, 0FFH, 45H + 8 * ORD(param2 = -1) + long(n)); // inc/dec qword[rbp+n] OutIntByte(n) END ELSE IF reg1 # -1 THEN addrc(reg1, param2) ELSE n := param1 * 8; OutByte3(48H, 81H + short(param2), 45H + long(n)); OutIntByte(n); OutIntByte(param2) // add qword[rbp+n],param2 END END |IL.opLADR_INCCB, IL.opLADR_DECCB: reg1 := GetVarReg(param1); param2 := param2 MOD 256; IF reg1 # -1 THEN IF opcode = IL.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(opcode = IL.opLADR_DECCB)); OutIntByte(n); OutByte(param2) // add/sub byte[rbp+n],param2 END |IL.opLADR_INC, IL.opLADR_DEC: UnOp(reg1); reg2 := GetVarReg(param2); IF reg2 # -1 THEN IF opcode = IL.opLADR_DEC THEN sub(reg2, reg1) ELSE add(reg2, reg1) END ELSE n := param2 * 8; Rex(0, reg1); OutByte2(01H + 28H * ORD(opcode = IL.opLADR_DEC), 45H + long(n) + (reg1 MOD 8) * 8); OutIntByte(n) // add/sub qword[rbp+n],reg1 END; drop |IL.opLADR_INCB, IL.opLADR_DECB: UnOp(reg1); reg2 := GetVarReg(param2); IF reg2 # -1 THEN IF opcode = IL.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(opcode = IL.opLADR_DECB), 45H + long(n) + 8 * (reg1 MOD 8)); OutIntByte(n) // add/sub byte[rbp+n], reg1_8 END; drop |IL.opLADR_INCL, IL.opLADR_EXCL: UnOp(reg1); cmprc(reg1, 64); reg2 := GetVarReg(param2); IF reg2 # -1 THEN OutByte2(73H, 4); // jnb L oprr2(0FH, 0ABH + 8 * ORD(opcode = IL.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(opcode = IL.opLADR_EXCL), 45H + long(n) + 8 * (reg1 MOD 8)); OutIntByte(n) // bts/btr qword[rbp+n], reg1 END; // L: drop |IL.opLADR_INCLC, IL.opLADR_EXCLC: reg1 := GetVarReg(param1); IF reg1 # -1 THEN Rex(reg1, 0); OutByte3(0FH, 0BAH, 0E8H); // bts/btr reg1, param2 OutByte2(reg1 MOD 8 + 8 * ORD(opcode = IL.opLADR_EXCLC), param2) ELSE n := param1 * 8; OutByte3(48H, 0FH, 0BAH); // bts/btr qword[rbp+n], param2 OutByte(6DH + long(n) + 8 * ORD(opcode = IL.opLADR_EXCLC)); OutIntByte(n); OutByte(param2) END |IL.opLOOP, IL.opENDLOOP: END; cmd := cmd.next(COMMAND) END; ASSERT(R.pushed = 0); ASSERT(R.top = -1); ASSERT(xmm = -1) END translate; PROCEDURE prolog (modname: ARRAY OF CHAR; target, stack_size: INTEGER); VAR ModName_Offs, entry, L: INTEGER; BEGIN ModName_Offs := tcount * 8 + CHL.Length(IL.codes.data); Numbers_Offs := ModName_Offs + LENGTH(modname) + 1; ASSERT(UTILS.Align(Numbers_Offs, 16)); entry := NewLabel(); X86.SetLabel(entry); IF target = mConst.Target_iDLL64 THEN dllret := NewLabel(); push(r8); push(rdx); push(rcx); CallRTL(IL._dllentry); test(rax); jcc(je, dllret) END; IF target = mConst.Target_iELF64 THEN push(rsp) ELSE pushc(0) END; lea(rax, entry, sCODE); push(rax); pushDA(0); //TYPES pushc(tcount); pushDA(ModName_Offs); //MODNAME CallRTL(IL._init); IF target IN {mConst.Target_iConsole64, mConst.Target_iGUI64, mConst.Target_iELF64} THEN L := NewLabel(); pushc(0); push(rsp); pushc(1024 * 1024 * stack_size); pushc(0); CallRTL(IL._new); pop(rax); test(rax); jcc(je, L); addrc(rax, 1024 * 1024 * stack_size - 8); mov(rsp, rax); X86.SetLabel(L) END END prolog; PROCEDURE epilog (modname: ARRAY OF CHAR; target: INTEGER); VAR i, n: INTEGER; number: Number; exp: IL.EXPORT_PROC; PROCEDURE import (imp: LISTS.LIST); VAR lib: IL.IMPORT_LIB; proc: IL.IMPORT_PROC; BEGIN lib := imp.first(IL.IMPORT_LIB); WHILE lib # NIL DO BIN.Import(prog, lib.name, 0); proc := lib.procs.first(IL.IMPORT_PROC); WHILE proc # NIL DO BIN.Import(prog, proc.name, proc.label); proc := proc.next(IL.IMPORT_PROC) END; lib := lib.next(IL.IMPORT_LIB) END END import; BEGIN IF target = mConst.Target_iDLL64 THEN X86.SetLabel(dllret); OutByte(0C3H) // ret ELSIF target = mConst.Target_iELFSO64 THEN sofinit := NewLabel(); OutByte(0C3H); // ret X86.SetLabel(sofinit); CallRTL(IL._sofinit); OutByte(0C3H) // ret ELSE pushc(0); CallRTL(IL._exit) END; X86.fixup; i := 0; WHILE i < tcount DO BIN.PutData64LE(prog, CHL.GetInt(IL.codes.types, i)); INC(i) END; i := 0; WHILE i < CHL.Length(IL.codes.data) DO BIN.PutData(prog, CHL.GetByte(IL.codes.data, i)); INC(i) END; BIN.PutDataStr(prog, modname); BIN.PutData(prog, 0); n := CHL.Length(prog.data); ASSERT(UTILS.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 := IL.codes.export.first(IL.EXPORT_PROC); WHILE exp # NIL DO BIN.Export(prog, exp.name, exp.label); exp := exp.next(IL.EXPORT_PROC) END; import(IL.codes.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* (outname: ARRAY OF CHAR; target: INTEGER; options: PROG.OPTIONS); VAR path, modname, ext: PATHS.PATH; BEGIN tcount := CHL.Length(IL.codes.types); 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); REG.Init(R, push, pop, mov, xchg, rload, rsave, {rax, r10, r11}, {rcx, rdx, r8, r9}); IL.set_bss(MAX(IL.codes.bss, MAX(IL.codes.dmin - CHL.Length(IL.codes.data), 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(IL.codes.lcount); BIN.SetParams(prog, IL.codes.bss, 1, WCHR(1), WCHR(0)); X86.SetProgram(prog); prolog(modname, target, options.stack); translate(IL.codes.commands, tcount * 8); epilog(modname, target); BIN.fixup(prog); IF target IN {mConst.Target_iConsole64, mConst.Target_iGUI64, mConst.Target_iDLL64} THEN PE32.write(prog, outname, target = mConst.Target_iConsole64, target = mConst.Target_iDLL64, TRUE) ELSIF target IN {mConst.Target_iELF64, mConst.Target_iELFSO64} THEN ELF.write(prog, outname, sofinit, target = mConst.Target_iELFSO64, TRUE) END END CodeGen; END AMD64.