| |
| /*---------------------------------------------------------------*/ |
| /*--- begin host_tilegx_defs.c ---*/ |
| /*---------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2010-2015 Tilera Corp. |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| |
| /* Contributed by Zhi-Gang Liu <zliu at tilera dot com> */ |
| |
| #include "libvex_basictypes.h" |
| #include "libvex.h" |
| #include "libvex_trc_values.h" |
| |
| #include "main_util.h" |
| #include "host_generic_regs.h" |
| #include "host_tilegx_defs.h" |
| #include "tilegx_disasm.h" |
| |
| /* Contributed by Zhi-Gang Liu <zliu at tilera dot com> */ |
| |
| /* Register number for guest state pointer in host code, r50 */ |
| #define GuestSP ( 50) |
| /* CONTEXT_EX0 offset */ |
| #define OFFSET_EX0 (576) |
| /* CONTEXT_EX1 offset */ |
| #define OFFSET_EX1 (584) |
| /* COND offset */ |
| #define OFFSET_COND (608) |
| /* PC offset */ |
| #define OFFSET_PC (512) |
| |
| /* guest_COND offset. */ |
| #define COND_OFFSET() OFFSET_COND |
| |
| /*---------------- Registers ----------------*/ |
| |
| void ppHRegTILEGX ( HReg reg ) |
| { |
| Int r; |
| static const HChar *ireg_names[64] = { |
| "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", |
| "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", |
| "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", |
| "r30", "r31", "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", |
| "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", "r48", "r49", |
| "r50", "r51", "r52", "r53", "r54", "r55", |
| "sn", "idn0", "idn1", "udn0", "udn1", "udn2", "udn3", "zero" |
| }; |
| |
| /* Be generic for all virtual regs. */ |
| if (hregIsVirtual(reg)) { |
| ppHReg(reg); |
| return; |
| } |
| |
| /* But specific for real regs. */ |
| vassert(hregClass(reg) == HRcInt32 || hregClass(reg) == HRcInt64 || |
| hregClass(reg) == HRcFlt32 || hregClass(reg) == HRcFlt64); |
| |
| /* But specific for real regs. */ |
| switch (hregClass(reg)) { |
| case HRcInt32: |
| case HRcInt64: |
| r = hregEncoding(reg); |
| vassert(r >= 0 && r < 64); |
| vex_printf("%s", ireg_names[r]); |
| return; |
| case HRcFlt32: |
| r = hregEncoding(reg); |
| vassert(r >= 0 && r < 64); |
| vex_printf("%s", ireg_names[r]); |
| return; |
| case HRcFlt64: |
| r = hregEncoding(reg); |
| vassert(r >= 0 && r < 64); |
| vex_printf("%s", ireg_names[r]); |
| return; |
| default: |
| vpanic("ppHRegTILEGX"); |
| } |
| |
| return; |
| } |
| |
| static const HChar* tilegxUnaryOp [] = |
| { |
| "clz ", |
| "ctz ", |
| "nop " |
| }; |
| |
| static const HChar* tilegxAluOp [] = |
| { "Alu_invalid", |
| "Add ", |
| "Sub ", |
| "And ", |
| "Or ", |
| "Nor ", |
| "Xor " |
| }; |
| |
| static const HChar* tilegxShftOp [] = |
| { |
| "Shft_invalid", |
| "Sll ", |
| "Srl ", |
| "Sra ", |
| "Sll8x8 ", |
| "Srl8x8 ", |
| }; |
| |
| static const HChar* tilegxBfOp [] = |
| { |
| "BfExts ", |
| "BfEtxu ", |
| "BfIns " |
| }; |
| |
| |
| static const HChar* tilegxAcasOp [] = |
| { |
| "CmpExch ", |
| "Exch ", |
| "FetchAnd ", |
| "FetchAdd ", |
| "FetchAddgez", |
| "FetchOr " |
| }; |
| |
| static const HChar* tilegxInstrTag [] = |
| { |
| "Imm ", |
| "ALU ", |
| "Shift ", |
| "Unary ", |
| "Cmp ", |
| "CmpI ", |
| "Mul ", |
| "Call ", |
| "XDirect ", |
| "XIndir ", |
| "XAssisted", |
| "EvCheck ", |
| "ProfInc ", |
| "RdWrLR ", |
| "Load ", |
| "Store ", |
| "MovCond ", |
| "BitField ", |
| "ACAS " |
| }; |
| |
| /* -------- Pretty Print instructions ------------- */ |
| static void ppLoadImm ( HReg dst, ULong imm ) |
| { |
| vex_printf("li "); |
| ppHRegTILEGX(dst); |
| vex_printf(",0x%016lx", (unsigned long)imm); |
| } |
| |
| void ppTILEGXInstr ( const TILEGXInstr * instr ) |
| { |
| vex_printf("%s ", tilegxInstrTag[instr->tag]); |
| switch (instr->tag) { |
| case GXin_LI: { |
| ppHRegTILEGX(instr->GXin.LI.dst); |
| vex_printf(",0x%016llx", instr->GXin.LI.imm); |
| } |
| break; |
| |
| case GXin_Alu: { |
| HReg r_srcL = instr->GXin.Alu.srcL; |
| TILEGXRH *rh_srcR = instr->GXin.Alu.srcR; |
| /* generic */ |
| vex_printf("%s ", tilegxAluOp[instr->GXin.Alu.op]); |
| ppHRegTILEGX(instr->GXin.Alu.dst); |
| vex_printf(","); |
| ppHRegTILEGX(r_srcL); |
| vex_printf(","); |
| ppTILEGXRH(rh_srcR); |
| } |
| break; |
| |
| case GXin_Shft: { |
| HReg r_srcL = instr->GXin.Shft.srcL; |
| TILEGXRH *rh_srcR = instr->GXin.Shft.srcR; |
| vex_printf("%s ", tilegxShftOp[instr->GXin.Shft.op]); |
| ppHRegTILEGX(instr->GXin.Shft.dst); |
| vex_printf(","); |
| ppHRegTILEGX(r_srcL); |
| vex_printf(","); |
| ppTILEGXRH(rh_srcR); |
| } |
| break; |
| |
| case GXin_Unary: { |
| vex_printf("%s ", tilegxUnaryOp[instr->GXin.Unary.op]); |
| ppHRegTILEGX(instr->GXin.Unary.dst); |
| vex_printf(","); |
| ppHRegTILEGX(instr->GXin.Unary.src); |
| } |
| break; |
| |
| case GXin_Cmp: { |
| ppHRegTILEGX(instr->GXin.Cmp.dst); |
| vex_printf(" = %s ( ", showTILEGXCondCode(instr->GXin.Cmp.cond)); |
| ppHRegTILEGX(instr->GXin.Cmp.srcL); |
| vex_printf(", "); |
| ppHRegTILEGX(instr->GXin.Cmp.srcR); |
| vex_printf(" )"); |
| } |
| break; |
| |
| case GXin_CmpI: { |
| ppHRegTILEGX(instr->GXin.CmpI.dst); |
| vex_printf(" = %s ( ", showTILEGXCondCode(instr->GXin.CmpI.cond)); |
| ppHRegTILEGX(instr->GXin.CmpI.srcL); |
| vex_printf(", "); |
| ppTILEGXRH(instr->GXin.CmpI.srcR); |
| vex_printf(" )"); |
| } |
| break; |
| |
| case GXin_Mul: { |
| if (instr->GXin.Mul.widening == False) { |
| vex_printf("mul "); |
| ppHRegTILEGX(instr->GXin.Mul.dst); |
| vex_printf(", "); |
| ppHRegTILEGX(instr->GXin.Mul.srcL); |
| vex_printf(", "); |
| ppHRegTILEGX(instr->GXin.Mul.srcR); |
| |
| } else { |
| vex_printf("%s ", instr->GXin.Mul.syned ? "mull32s" : "mull32u"); |
| ppHRegTILEGX(instr->GXin.Mul.dst); |
| vex_printf(", "); |
| ppHRegTILEGX(instr->GXin.Mul.srcL); |
| vex_printf(", "); |
| ppHRegTILEGX(instr->GXin.Mul.srcR); |
| } |
| } |
| break; |
| |
| case GXin_Call: { |
| Int n; |
| if (instr->GXin.Call.cond != TILEGXcc_AL) { |
| vex_printf("if (%s (", showTILEGXCondCode(instr->GXin.Call.cond)); |
| ppHRegTILEGX(instr->GXin.Call.src); |
| vex_printf(",zero))"); |
| } |
| else |
| vex_printf("(always) "); |
| |
| vex_printf("{ "); |
| ppLoadImm(hregTILEGX_R11(), instr->GXin.Call.target); |
| |
| vex_printf(" ; ["); |
| for (n = 0; n < 56; n++) { |
| if (instr->GXin.Call.argiregs & (1ULL << n)) { |
| vex_printf("r%d", n); |
| if ((instr->GXin.Call.argiregs >> n) > 1) |
| vex_printf(","); |
| } |
| } |
| vex_printf("] }"); |
| } |
| break; |
| |
| case GXin_XDirect: |
| vex_printf("(xDirect) "); |
| vex_printf("if (guest_COND.%s) { ", |
| showTILEGXCondCode(instr->GXin.XDirect.cond)); |
| vex_printf("move r11, 0x%x,", (UInt)instr->GXin.XDirect.dstGA); |
| vex_printf("; st r11, "); |
| ppTILEGXAMode(instr->GXin.XDirect.amPC); |
| vex_printf("; move r11, $disp_cp_chain_me_to_%sEP; jalr r11; nop}", |
| instr->GXin.XDirect.toFastEP ? "fast" : "slow"); |
| return; |
| case GXin_XIndir: |
| vex_printf("(xIndir) "); |
| vex_printf("if (guest_COND.%s) { st ", |
| showTILEGXCondCode(instr->GXin.XIndir.cond)); |
| ppHRegTILEGX(instr->GXin.XIndir.dstGA); |
| vex_printf(", "); |
| ppTILEGXAMode(instr->GXin.XIndir.amPC); |
| vex_printf("; move r11, $disp_indir; jalr r11; nop}"); |
| return; |
| case GXin_XAssisted: |
| vex_printf("(xAssisted) "); |
| vex_printf("if (guest_COND.%s) { ", |
| showTILEGXCondCode(instr->GXin.XAssisted.cond)); |
| vex_printf("st "); |
| ppHRegTILEGX(instr->GXin.XAssisted.dstGA); |
| vex_printf(", "); |
| ppTILEGXAMode(instr->GXin.XAssisted.amPC); |
| vex_printf("; move r50, $IRJumpKind_to_TRCVAL(%d)", |
| (Int)instr->GXin.XAssisted.jk); |
| vex_printf("; move r11, $disp_assisted; jalr r11; nop; }"); |
| return; |
| |
| case GXin_EvCheck: |
| vex_printf("(evCheck) ld r11, "); |
| ppTILEGXAMode(instr->GXin.EvCheck.amCounter); |
| vex_printf("; addli r11, r11, -1"); |
| vex_printf("; st r11, "); |
| ppTILEGXAMode(instr->GXin.EvCheck.amCounter); |
| vex_printf("; bgez r11, nofail; jalr *"); |
| ppTILEGXAMode(instr->GXin.EvCheck.amFailAddr); |
| vex_printf("; nofail:"); |
| return; |
| case GXin_ProfInc: |
| vex_printf("(profInc) move r11, ($NotKnownYet); " |
| "ld r8, r11; " |
| "addi r8, r8, 1; " |
| "st r11, r8; " ); |
| return; |
| case GXin_Load: { |
| UChar sz = instr->GXin.Load.sz; |
| UChar c_sz = sz == 1 ? '1' : sz == 2 ? '2' : sz == 4 ? '4' : '8'; |
| vex_printf("ld%c ", c_sz); |
| ppHRegTILEGX(instr->GXin.Load.dst); |
| vex_printf(","); |
| ppTILEGXAMode(instr->GXin.Load.src); |
| } |
| break; |
| |
| case GXin_Store: { |
| UChar sz = instr->GXin.Store.sz; |
| UChar c_sz = sz == 1 ? '1' : sz == 2 ? '2' : sz == 4 ? '4' : '8'; |
| vex_printf("st%c ", c_sz); |
| ppTILEGXAMode(instr->GXin.Store.dst); |
| vex_printf(","); |
| ppHRegTILEGX(instr->GXin.Store.src); |
| } |
| break; |
| |
| case GXin_MovCond: { |
| ppHRegTILEGX(instr->GXin.MovCond.dst); |
| vex_printf("="); |
| showTILEGXCondCode(instr->GXin.MovCond.cond); |
| vex_printf("?"); |
| ppHRegTILEGX(instr->GXin.MovCond.srcL); |
| vex_printf(":"); |
| ppTILEGXRH(instr->GXin.MovCond.srcR); |
| } |
| break; |
| |
| case GXin_Acas: { |
| vex_printf("%s ", tilegxAcasOp[instr->GXin.Acas.op]); |
| ppHRegTILEGX(instr->GXin.Acas.old); |
| vex_printf(","); |
| if (instr->GXin.Acas.op == GXacas_CMPEXCH) { |
| ppHRegTILEGX(instr->GXin.Acas.exp); |
| vex_printf(","); |
| } |
| ppHRegTILEGX(instr->GXin.Acas.new); |
| } |
| break; |
| |
| case GXin_Bf: { |
| vex_printf("%s ", tilegxBfOp[instr->GXin.Bf.op]); |
| ppHRegTILEGX(instr->GXin.Bf.dst); |
| vex_printf(","); |
| ppHRegTILEGX(instr->GXin.Bf.src); |
| vex_printf(","); |
| vex_printf("%d,%d", (Int)instr->GXin.Bf.Start, (Int)instr->GXin.Bf.End); |
| } |
| break; |
| |
| default: |
| vassert(0); |
| } |
| } |
| |
| |
| const RRegUniverse* getRRegUniverse_TILEGX ( void ) |
| { |
| /* The 'universe' is constant and BIG, do it statically. */ |
| static RRegUniverse rRegUniverse_TILEGX; |
| static UInt rRegUniverse_TILEGX_initted = False; |
| |
| /* Get a pointer of the 'universe' */ |
| RRegUniverse* ru = &rRegUniverse_TILEGX; |
| |
| if (LIKELY(rRegUniverse_TILEGX_initted)) |
| return ru; |
| |
| RRegUniverse__init(ru); |
| |
| /* Callee saves ones are listed first, since we prefer them |
| if they're available */ |
| |
| ru->regs[ru->size++] = hregTILEGX_R30(); |
| ru->regs[ru->size++] = hregTILEGX_R31(); |
| ru->regs[ru->size++] = hregTILEGX_R32(); |
| ru->regs[ru->size++] = hregTILEGX_R33(); |
| ru->regs[ru->size++] = hregTILEGX_R34(); |
| ru->regs[ru->size++] = hregTILEGX_R35(); |
| ru->regs[ru->size++] = hregTILEGX_R36(); |
| ru->regs[ru->size++] = hregTILEGX_R37(); |
| ru->regs[ru->size++] = hregTILEGX_R38(); |
| ru->regs[ru->size++] = hregTILEGX_R39(); |
| |
| ru->regs[ru->size++] = hregTILEGX_R40(); |
| ru->regs[ru->size++] = hregTILEGX_R41(); |
| ru->regs[ru->size++] = hregTILEGX_R42(); |
| ru->regs[ru->size++] = hregTILEGX_R43(); |
| ru->regs[ru->size++] = hregTILEGX_R44(); |
| ru->regs[ru->size++] = hregTILEGX_R45(); |
| ru->regs[ru->size++] = hregTILEGX_R46(); |
| ru->regs[ru->size++] = hregTILEGX_R47(); |
| ru->regs[ru->size++] = hregTILEGX_R48(); |
| ru->regs[ru->size++] = hregTILEGX_R49(); |
| |
| /* GPR 50 is reserved as Guest state */ |
| /* GPR 51 is reserved register, mainly used to do memory |
| load and store since TileGx has no pre-displacement |
| addressing mode */ |
| |
| ru->regs[ru->size++] = hregTILEGX_R10(); |
| |
| /* GPR 11 is reserved as next guest address */ |
| |
| ru->regs[ru->size++] = hregTILEGX_R13(); |
| ru->regs[ru->size++] = hregTILEGX_R14(); |
| ru->regs[ru->size++] = hregTILEGX_R15(); |
| ru->regs[ru->size++] = hregTILEGX_R16(); |
| ru->regs[ru->size++] = hregTILEGX_R17(); |
| ru->regs[ru->size++] = hregTILEGX_R18(); |
| ru->regs[ru->size++] = hregTILEGX_R19(); |
| ru->regs[ru->size++] = hregTILEGX_R20(); |
| ru->regs[ru->size++] = hregTILEGX_R21(); |
| ru->regs[ru->size++] = hregTILEGX_R22(); |
| ru->regs[ru->size++] = hregTILEGX_R23(); |
| ru->regs[ru->size++] = hregTILEGX_R24(); |
| ru->regs[ru->size++] = hregTILEGX_R25(); |
| ru->regs[ru->size++] = hregTILEGX_R26(); |
| ru->regs[ru->size++] = hregTILEGX_R27(); |
| ru->regs[ru->size++] = hregTILEGX_R28(); |
| ru->regs[ru->size++] = hregTILEGX_R29(); |
| |
| ru->allocable = ru->size; |
| |
| /* And other unallocable registers. */ |
| ru->regs[ru->size++] = hregTILEGX_R0(); |
| ru->regs[ru->size++] = hregTILEGX_R1(); |
| ru->regs[ru->size++] = hregTILEGX_R2(); |
| ru->regs[ru->size++] = hregTILEGX_R3(); |
| ru->regs[ru->size++] = hregTILEGX_R4(); |
| ru->regs[ru->size++] = hregTILEGX_R5(); |
| ru->regs[ru->size++] = hregTILEGX_R6(); |
| ru->regs[ru->size++] = hregTILEGX_R7(); |
| ru->regs[ru->size++] = hregTILEGX_R8(); |
| ru->regs[ru->size++] = hregTILEGX_R9(); |
| ru->regs[ru->size++] = hregTILEGX_R11(); |
| ru->regs[ru->size++] = hregTILEGX_R12(); |
| ru->regs[ru->size++] = hregTILEGX_R50(); |
| ru->regs[ru->size++] = hregTILEGX_R51(); |
| ru->regs[ru->size++] = hregTILEGX_R52(); |
| ru->regs[ru->size++] = hregTILEGX_R53(); |
| ru->regs[ru->size++] = hregTILEGX_R54(); |
| ru->regs[ru->size++] = hregTILEGX_R55(); |
| ru->regs[ru->size++] = hregTILEGX_R63(); |
| |
| rRegUniverse_TILEGX_initted = True; |
| |
| RRegUniverse__check_is_sane(ru); |
| |
| return ru; |
| } |
| |
| /*----------------- Condition Codes ----------------------*/ |
| |
| const HChar *showTILEGXCondCode ( TILEGXCondCode cond ) |
| { |
| switch (cond) { |
| case TILEGXcc_EQ: |
| return "e"; /* equal */ |
| case TILEGXcc_EQ8x8: |
| return "e8x8"; /* equal */ |
| |
| case TILEGXcc_NE: |
| return "ne"; /* not equal */ |
| case TILEGXcc_NE8x8: |
| return "ne8x8"; /* not equal */ |
| |
| case TILEGXcc_HS: |
| return "hs"; /* >=u (higher or same) */ |
| case TILEGXcc_LO: |
| return "lo"; /* <u (lower) */ |
| |
| case TILEGXcc_MI: |
| return "mi"; /* minus (negative) */ |
| case TILEGXcc_PL: |
| return "pl"; /* plus (zero or +ve) */ |
| |
| case TILEGXcc_VS: |
| return "vs"; /* overflow */ |
| case TILEGXcc_VC: |
| return "vc"; /* no overflow */ |
| |
| case TILEGXcc_HI: |
| return "hi"; /* >u (higher) */ |
| case TILEGXcc_LS: |
| return "ls"; /* <=u (lower or same) */ |
| |
| case TILEGXcc_GE: |
| return "ge"; /* >=s (signed greater or equal) */ |
| case TILEGXcc_LT: |
| return "lt"; /* <s (signed less than) */ |
| |
| case TILEGXcc_GT: |
| return "gt"; /* >s (signed greater) */ |
| case TILEGXcc_LE: |
| return "le"; /* <=s (signed less or equal) */ |
| |
| case TILEGXcc_AL: |
| return "al"; /* always (unconditional) */ |
| case TILEGXcc_NV: |
| return "nv"; /* never (unconditional): */ |
| case TILEGXcc_EZ: |
| return "ez"; /* equal 0 */ |
| case TILEGXcc_NZ: |
| return "nz"; /* not equal 0 */ |
| |
| default: |
| vpanic("showTILEGXCondCode"); |
| } |
| } |
| |
| |
| /* --------- TILEGXAMode: memory address expressions. --------- */ |
| |
| TILEGXAMode *TILEGXAMode_IR ( Int idx, HReg base ) |
| { |
| TILEGXAMode *am = LibVEX_Alloc(sizeof(TILEGXAMode)); |
| am->tag = GXam_IR; |
| am->GXam.IR.base = base; |
| am->GXam.IR.index = idx; |
| |
| return am; |
| } |
| |
| TILEGXAMode *nextTILEGXAModeInt ( TILEGXAMode * am ) |
| { |
| if (am->tag == GXam_IR) |
| return TILEGXAMode_IR(am->GXam.IR.index + 4, am->GXam.IR.base); |
| |
| vpanic("dopyTILEGXAMode"); |
| } |
| |
| void ppTILEGXAMode ( const TILEGXAMode * am ) |
| { |
| if (am->tag == GXam_IR) |
| { |
| if (am->GXam.IR.index == 0) |
| vex_printf("("); |
| else |
| vex_printf("%d(", (Int) am->GXam.IR.index); |
| ppHRegTILEGX(am->GXam.IR.base); |
| vex_printf(")"); |
| return; |
| } |
| vpanic("ppTILEGXAMode"); |
| } |
| |
| static void addRegUsage_TILEGXAMode ( HRegUsage * u, TILEGXAMode * am ) |
| { |
| if (am->tag == GXam_IR) |
| { |
| addHRegUse(u, HRmRead, am->GXam.IR.base); |
| return; |
| } |
| |
| vpanic("addRegUsage_TILEGXAMode"); |
| } |
| |
| static void mapRegs_TILEGXAMode ( HRegRemap * m, TILEGXAMode * am ) |
| { |
| if (am->tag == GXam_IR) |
| { |
| am->GXam.IR.base = lookupHRegRemap(m, am->GXam.IR.base); |
| return; |
| } |
| |
| vpanic("mapRegs_TILEGXAMode"); |
| } |
| |
| /* --------- Operand, which can be a reg or a u16/s16. --------- */ |
| |
| TILEGXRH *TILEGXRH_Imm ( Bool syned, UShort imm16 ) |
| { |
| TILEGXRH *op = LibVEX_Alloc(sizeof(TILEGXRH)); |
| op->tag = GXrh_Imm; |
| op->GXrh.Imm.syned = syned; |
| op->GXrh.Imm.imm16 = imm16; |
| /* If this is a signed value, ensure it's not -32768, so that we |
| are guaranteed always to be able to negate if needed. */ |
| if (syned) |
| vassert(imm16 != 0x8000); |
| vassert(syned == True || syned == False); |
| return op; |
| } |
| |
| TILEGXRH *TILEGXRH_Reg ( HReg reg ) |
| { |
| TILEGXRH *op = LibVEX_Alloc(sizeof(TILEGXRH)); |
| op->tag = GXrh_Reg; |
| op->GXrh.Reg.reg = reg; |
| return op; |
| } |
| |
| void ppTILEGXRH ( const TILEGXRH * op ) |
| { |
| TILEGXRHTag tag = op->tag; |
| switch (tag) { |
| case GXrh_Imm: |
| if (op->GXrh.Imm.syned) |
| vex_printf("%d", (Int) (Short) op->GXrh.Imm.imm16); |
| else |
| vex_printf("%u", (UInt) (UShort) op->GXrh.Imm.imm16); |
| return; |
| case GXrh_Reg: |
| ppHRegTILEGX(op->GXrh.Reg.reg); |
| return; |
| default: |
| vpanic("ppTILEGXRH"); |
| } |
| } |
| |
| /* An TILEGXRH can only be used in a "read" context (what would it mean |
| to write or modify a literal?) and so we enumerate its registers |
| accordingly. */ |
| static void addRegUsage_TILEGXRH ( HRegUsage * u, TILEGXRH * op ) |
| { |
| switch (op->tag) { |
| case GXrh_Imm: |
| return; |
| case GXrh_Reg: |
| addHRegUse(u, HRmRead, op->GXrh.Reg.reg); |
| return; |
| default: |
| vpanic("addRegUsage_TILEGXRH"); |
| } |
| } |
| |
| static void mapRegs_TILEGXRH ( HRegRemap * m, TILEGXRH * op ) |
| { |
| switch (op->tag) { |
| case GXrh_Imm: |
| return; |
| case GXrh_Reg: |
| op->GXrh.Reg.reg = lookupHRegRemap(m, op->GXrh.Reg.reg); |
| return; |
| default: |
| vpanic("mapRegs_TILEGXRH"); |
| } |
| } |
| |
| TILEGXInstr *TILEGXInstr_LI ( HReg dst, ULong imm ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_LI; |
| i->GXin.LI.dst = dst; |
| i->GXin.LI.imm = imm; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Alu ( TILEGXAluOp op, HReg dst, HReg srcL, |
| TILEGXRH * srcR ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Alu; |
| i->GXin.Alu.op = op; |
| i->GXin.Alu.dst = dst; |
| i->GXin.Alu.srcL = srcL; |
| i->GXin.Alu.srcR = srcR; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Shft ( TILEGXShftOp op, Bool sz32, HReg dst, HReg srcL, |
| TILEGXRH * srcR ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Shft; |
| i->GXin.Shft.op = op; |
| i->GXin.Shft.sz32 = sz32; |
| i->GXin.Shft.dst = dst; |
| i->GXin.Shft.srcL = srcL; |
| i->GXin.Shft.srcR = srcR; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Unary ( TILEGXUnaryOp op, HReg dst, HReg src ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Unary; |
| i->GXin.Unary.op = op; |
| i->GXin.Unary.dst = dst; |
| i->GXin.Unary.src = src; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Cmp ( Bool syned, Bool sz32, HReg dst, |
| HReg srcL, HReg srcR, TILEGXCondCode cond ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Cmp; |
| i->GXin.Cmp.syned = syned; |
| i->GXin.Cmp.sz32 = sz32; |
| i->GXin.Cmp.dst = dst; |
| i->GXin.Cmp.srcL = srcL; |
| i->GXin.Cmp.srcR = srcR; |
| i->GXin.Cmp.cond = cond; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_CmpI ( Bool syned, Bool sz32, HReg dst, |
| HReg srcL, TILEGXRH * srcR, |
| TILEGXCondCode cond ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_CmpI; |
| i->GXin.CmpI.syned = syned; |
| i->GXin.CmpI.sz32 = sz32; |
| i->GXin.CmpI.dst = dst; |
| i->GXin.CmpI.srcL = srcL; |
| i->GXin.CmpI.srcR = srcR; |
| i->GXin.CmpI.cond = cond; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Bf ( TILEGXBfOp op, HReg dst, HReg src, |
| UInt Start, UInt End ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Bf; |
| i->GXin.Bf.op = op; |
| i->GXin.Bf.dst = dst; |
| i->GXin.Bf.src = src; |
| i->GXin.Bf.Start = Start; |
| i->GXin.Bf.End = End; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Acas ( TILEGXAcasOp op, HReg old, |
| HReg addr, HReg exp, HReg new, UInt sz ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Acas; |
| i->GXin.Acas.op = op; |
| i->GXin.Acas.old = old; |
| i->GXin.Acas.addr = addr; |
| i->GXin.Acas.exp = exp; |
| i->GXin.Acas.new = new; |
| i->GXin.Acas.sz = sz; |
| return i; |
| } |
| |
| /* multiply */ |
| TILEGXInstr *TILEGXInstr_Mul ( Bool syned, Bool wid, Bool sz32, |
| HReg dst, HReg srcL, |
| HReg srcR ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Mul; |
| i->GXin.Mul.syned = syned; |
| i->GXin.Mul.widening = wid; /* widen=True else False */ |
| i->GXin.Mul.sz32 = sz32; /* True = 32 bits */ |
| i->GXin.Mul.dst = dst; |
| i->GXin.Mul.srcL = srcL; |
| i->GXin.Mul.srcR = srcR; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Call ( TILEGXCondCode cond, Addr64 target, |
| ULong argiregs, |
| HReg src ) |
| { |
| ULong mask; |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Call; |
| i->GXin.Call.cond = cond; |
| i->GXin.Call.target = target; |
| i->GXin.Call.argiregs = argiregs; |
| i->GXin.Call.src = src; |
| |
| /* Only r0 .. r9 inclusive may be used as arg regs. Hence: */ |
| mask = (1ULL << 10) - 1; |
| vassert(0 == (argiregs & ~mask)); |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_CallAlways ( TILEGXCondCode cond, Addr64 target, |
| ULong argiregs ) |
| { |
| ULong mask; |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Call; |
| i->GXin.Call.cond = cond; |
| i->GXin.Call.target = target; |
| i->GXin.Call.argiregs = argiregs; |
| |
| /* Only r0 .. r9 inclusive may be used as arg regs. Hence: */ |
| mask = (1ULL << 10) - 1; |
| vassert(0 == (argiregs & ~mask)); |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_XDirect ( Addr64 dstGA, TILEGXAMode* amPC, |
| TILEGXCondCode cond, Bool toFastEP ) |
| { |
| TILEGXInstr* i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_XDirect; |
| i->GXin.XDirect.dstGA = dstGA; |
| i->GXin.XDirect.amPC = amPC; |
| i->GXin.XDirect.cond = cond; |
| i->GXin.XDirect.toFastEP = toFastEP; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_XIndir ( HReg dstGA, TILEGXAMode* amPC, |
| TILEGXCondCode cond ) |
| { |
| TILEGXInstr* i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_XIndir; |
| i->GXin.XIndir.dstGA = dstGA; |
| i->GXin.XIndir.amPC = amPC; |
| i->GXin.XIndir.cond = cond; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_XAssisted ( HReg dstGA, TILEGXAMode* amPC, |
| TILEGXCondCode cond, IRJumpKind jk ) |
| { |
| TILEGXInstr* i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_XAssisted; |
| i->GXin.XAssisted.dstGA = dstGA; |
| i->GXin.XAssisted.amPC = amPC; |
| i->GXin.XAssisted.cond = cond; |
| i->GXin.XAssisted.jk = jk; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_EvCheck ( TILEGXAMode* amCounter, |
| TILEGXAMode* amFailAddr ) { |
| TILEGXInstr* i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_EvCheck; |
| i->GXin.EvCheck.amCounter = amCounter; |
| i->GXin.EvCheck.amFailAddr = amFailAddr; |
| return i; |
| } |
| |
| TILEGXInstr* TILEGXInstr_ProfInc ( void ) { |
| TILEGXInstr* i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_ProfInc; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Load ( UChar sz, HReg dst, TILEGXAMode * src ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Load; |
| i->GXin.Load.sz = sz; |
| i->GXin.Load.src = src; |
| i->GXin.Load.dst = dst; |
| vassert(sz == 1 || sz == 2 || sz == 4 || sz == 8); |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_Store(UChar sz, TILEGXAMode * dst, HReg src) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_Store; |
| i->GXin.Store.sz = sz; |
| i->GXin.Store.src = src; |
| i->GXin.Store.dst = dst; |
| vassert(sz == 1 || sz == 2 || sz == 4 || sz == 8); |
| return i; |
| } |
| |
| /* Read/Write Link Register */ |
| TILEGXInstr *TILEGXInstr_RdWrLR ( Bool wrLR, HReg gpr ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_RdWrLR; |
| i->GXin.RdWrLR.wrLR = wrLR; |
| i->GXin.RdWrLR.gpr = gpr; |
| return i; |
| } |
| |
| TILEGXInstr *TILEGXInstr_MovCond ( HReg dst, HReg argL, TILEGXRH * argR, |
| HReg condR, TILEGXCondCode cond ) |
| { |
| TILEGXInstr *i = LibVEX_Alloc(sizeof(TILEGXInstr)); |
| i->tag = GXin_MovCond; |
| i->GXin.MovCond.dst = dst; |
| i->GXin.MovCond.srcL = argL; |
| i->GXin.MovCond.srcR = argR; |
| i->GXin.MovCond.condR = condR; |
| i->GXin.MovCond.cond = cond; |
| return i; |
| } |
| |
| /* --------- Helpers for register allocation. --------- */ |
| |
| void getRegUsage_TILEGXInstr ( HRegUsage * u, TILEGXInstr * i ) |
| { |
| initHRegUsage(u); |
| switch (i->tag) { |
| case GXin_LI: |
| addHRegUse(u, HRmWrite, i->GXin.LI.dst); |
| break; |
| case GXin_Alu: |
| addHRegUse(u, HRmRead, i->GXin.Alu.srcL); |
| addRegUsage_TILEGXRH(u, i->GXin.Alu.srcR); |
| addHRegUse(u, HRmWrite, i->GXin.Alu.dst); |
| return; |
| case GXin_CmpI: |
| addHRegUse(u, HRmRead, i->GXin.CmpI.srcL); |
| addRegUsage_TILEGXRH(u, i->GXin.CmpI.srcR); |
| addHRegUse(u, HRmWrite, i->GXin.CmpI.dst); |
| return; |
| case GXin_Shft: |
| addHRegUse(u, HRmRead, i->GXin.Shft.srcL); |
| addRegUsage_TILEGXRH(u, i->GXin.Shft.srcR); |
| addHRegUse(u, HRmWrite, i->GXin.Shft.dst); |
| return; |
| case GXin_Cmp: |
| addHRegUse(u, HRmRead, i->GXin.Cmp.srcL); |
| addHRegUse(u, HRmRead, i->GXin.Cmp.srcR); |
| addHRegUse(u, HRmWrite, i->GXin.Cmp.dst); |
| return; |
| case GXin_Bf: |
| addHRegUse(u, HRmRead, i->GXin.Bf.src); |
| addHRegUse(u, HRmWrite, i->GXin.Bf.dst); |
| return; |
| case GXin_Acas: |
| addHRegUse(u, HRmRead, i->GXin.Acas.addr); |
| addHRegUse(u, HRmRead, i->GXin.Acas.new); |
| if (i->GXin.Acas.op == GXacas_CMPEXCH) |
| addHRegUse(u, HRmRead, i->GXin.Acas.exp); |
| addHRegUse(u, HRmWrite, i->GXin.Acas.old); |
| return; |
| case GXin_Unary: |
| addHRegUse(u, HRmRead, i->GXin.Unary.src); |
| addHRegUse(u, HRmWrite, i->GXin.Unary.dst); |
| return; |
| case GXin_Mul: |
| addHRegUse(u, HRmWrite, i->GXin.Mul.dst); |
| addHRegUse(u, HRmRead, i->GXin.Mul.srcL); |
| addHRegUse(u, HRmRead, i->GXin.Mul.srcR); |
| return; |
| case GXin_Call: { |
| if (i->GXin.Call.cond != TILEGXcc_AL) |
| addHRegUse(u, HRmRead, i->GXin.Call.src); |
| ULong argir; |
| |
| // Only need save r10-r29, and r0-r9 is not allocable. |
| addHRegUse(u, HRmWrite, hregTILEGX_R10()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R11()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R12()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R13()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R14()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R15()); |
| |
| addHRegUse(u, HRmWrite, hregTILEGX_R16()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R17()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R18()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R19()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R20()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R21()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R22()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R23()); |
| |
| addHRegUse(u, HRmWrite, hregTILEGX_R24()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R25()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R26()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R27()); |
| |
| addHRegUse(u, HRmWrite, hregTILEGX_R28()); |
| addHRegUse(u, HRmWrite, hregTILEGX_R29()); |
| |
| /* Now we have to state any parameter-carrying registers |
| which might be read. This depends on the argiregs field. */ |
| argir = i->GXin.Call.argiregs; |
| if (argir & (1 << 9)) |
| addHRegUse(u, HRmRead, hregTILEGX_R9()); |
| if (argir & (1 << 8)) |
| addHRegUse(u, HRmRead, hregTILEGX_R8()); |
| if (argir & (1 << 7)) |
| addHRegUse(u, HRmRead, hregTILEGX_R7()); |
| if (argir & (1 << 6)) |
| addHRegUse(u, HRmRead, hregTILEGX_R6()); |
| if (argir & (1 << 5)) |
| addHRegUse(u, HRmRead, hregTILEGX_R5()); |
| if (argir & (1 << 4)) |
| addHRegUse(u, HRmRead, hregTILEGX_R4()); |
| if (argir & (1 << 3)) |
| addHRegUse(u, HRmRead, hregTILEGX_R3()); |
| if (argir & (1 << 2)) |
| addHRegUse(u, HRmRead, hregTILEGX_R2()); |
| if (argir & (1 << 1)) |
| addHRegUse(u, HRmRead, hregTILEGX_R1()); |
| if (argir & (1 << 0)) |
| addHRegUse(u, HRmRead, hregTILEGX_R0()); |
| |
| vassert(0 == (argir & ~((1ULL << 10) - 1))); |
| return; |
| } |
| case GXin_XDirect: |
| addRegUsage_TILEGXAMode(u, i->GXin.XDirect.amPC); |
| return; |
| case GXin_XIndir: |
| addHRegUse(u, HRmRead, i->GXin.XIndir.dstGA); |
| addRegUsage_TILEGXAMode(u, i->GXin.XIndir.amPC); |
| return; |
| case GXin_XAssisted: |
| addHRegUse(u, HRmRead, i->GXin.XAssisted.dstGA); |
| addRegUsage_TILEGXAMode(u, i->GXin.XAssisted.amPC); |
| return; |
| |
| case GXin_EvCheck: |
| addRegUsage_TILEGXAMode(u, i->GXin.EvCheck.amCounter); |
| addRegUsage_TILEGXAMode(u, i->GXin.EvCheck.amFailAddr); |
| return; |
| case GXin_ProfInc: |
| return; |
| case GXin_Load: |
| addRegUsage_TILEGXAMode(u, i->GXin.Load.src); |
| addHRegUse(u, HRmWrite, i->GXin.Load.dst); |
| return; |
| case GXin_Store: |
| addHRegUse(u, HRmRead, i->GXin.Store.src); |
| addRegUsage_TILEGXAMode(u, i->GXin.Store.dst); |
| return; |
| case GXin_RdWrLR: |
| addHRegUse(u, (i->GXin.RdWrLR.wrLR ? HRmRead : HRmWrite), |
| i->GXin.RdWrLR.gpr); |
| return; |
| case GXin_MovCond: |
| if (i->GXin.MovCond.srcR->tag == GXrh_Reg) { |
| addHRegUse(u, HRmRead, i->GXin.MovCond.srcR->GXrh.Reg.reg); |
| } |
| addHRegUse(u, HRmRead, i->GXin.MovCond.srcL); |
| addHRegUse(u, HRmRead, i->GXin.MovCond.condR); |
| addHRegUse(u, HRmWrite, i->GXin.MovCond.dst); |
| return; |
| default: |
| vpanic("getRegUsage_TILEGXInstr"); |
| } |
| } |
| |
| /* local helper */ |
| static void mapReg ( HRegRemap * m, HReg * r ) |
| { |
| *r = lookupHRegRemap(m, *r); |
| } |
| |
| void mapRegs_TILEGXInstr ( HRegRemap * m, TILEGXInstr * i ) |
| { |
| switch (i->tag) { |
| case GXin_LI: |
| mapReg(m, &i->GXin.LI.dst); |
| break; |
| case GXin_Alu: |
| mapReg(m, &i->GXin.Alu.srcL); |
| mapRegs_TILEGXRH(m, i->GXin.Alu.srcR); |
| mapReg(m, &i->GXin.Alu.dst); |
| return; |
| case GXin_CmpI: |
| mapReg(m, &i->GXin.CmpI.srcL); |
| mapRegs_TILEGXRH(m, i->GXin.CmpI.srcR); |
| mapReg(m, &i->GXin.CmpI.dst); |
| return; |
| case GXin_Shft: |
| mapReg(m, &i->GXin.Shft.srcL); |
| mapRegs_TILEGXRH(m, i->GXin.Shft.srcR); |
| mapReg(m, &i->GXin.Shft.dst); |
| return; |
| case GXin_Cmp: |
| mapReg(m, &i->GXin.Cmp.srcL); |
| mapReg(m, &i->GXin.Cmp.srcR); |
| mapReg(m, &i->GXin.Cmp.dst); |
| return; |
| case GXin_Acas: |
| mapReg(m, &i->GXin.Acas.old); |
| mapReg(m, &i->GXin.Acas.addr); |
| mapReg(m, &i->GXin.Acas.new); |
| if (i->GXin.Acas.op == GXacas_CMPEXCH) |
| mapReg(m, &i->GXin.Acas.exp); |
| return; |
| case GXin_Bf: |
| mapReg(m, &i->GXin.Bf.src); |
| mapReg(m, &i->GXin.Bf.dst); |
| return; |
| case GXin_Unary: |
| mapReg(m, &i->GXin.Unary.src); |
| mapReg(m, &i->GXin.Unary.dst); |
| return; |
| case GXin_Mul: |
| mapReg(m, &i->GXin.Mul.dst); |
| mapReg(m, &i->GXin.Mul.srcL); |
| mapReg(m, &i->GXin.Mul.srcR); |
| return; |
| case GXin_Call: |
| { |
| if (i->GXin.Call.cond != TILEGXcc_AL) |
| mapReg(m, &i->GXin.Call.src); |
| return; |
| } |
| case GXin_XDirect: |
| mapRegs_TILEGXAMode(m, i->GXin.XDirect.amPC); |
| return; |
| case GXin_XIndir: |
| mapReg(m, &i->GXin.XIndir.dstGA); |
| mapRegs_TILEGXAMode(m, i->GXin.XIndir.amPC); |
| return; |
| case GXin_XAssisted: |
| mapReg(m, &i->GXin.XAssisted.dstGA); |
| mapRegs_TILEGXAMode(m, i->GXin.XAssisted.amPC); |
| return; |
| case GXin_EvCheck: |
| mapRegs_TILEGXAMode(m, i->GXin.EvCheck.amCounter); |
| mapRegs_TILEGXAMode(m, i->GXin.EvCheck.amFailAddr); |
| return; |
| case GXin_ProfInc: |
| return; |
| case GXin_Load: |
| mapRegs_TILEGXAMode(m, i->GXin.Load.src); |
| mapReg(m, &i->GXin.Load.dst); |
| return; |
| case GXin_Store: |
| mapReg(m, &i->GXin.Store.src); |
| mapRegs_TILEGXAMode(m, i->GXin.Store.dst); |
| return; |
| case GXin_RdWrLR: |
| mapReg(m, &i->GXin.RdWrLR.gpr); |
| return; |
| case GXin_MovCond: |
| if (i->GXin.MovCond.srcR->tag == GXrh_Reg) { |
| mapReg(m, &(i->GXin.MovCond.srcR->GXrh.Reg.reg)); |
| } |
| mapReg(m, &i->GXin.MovCond.srcL); |
| mapReg(m, &i->GXin.MovCond.condR); |
| mapReg(m, &i->GXin.MovCond.dst); |
| |
| return; |
| default: |
| vpanic("mapRegs_TILEGXInstr"); |
| } |
| } |
| |
| /* Figure out if i represents a reg-reg move, and if so assign the |
| source and destination to *src and *dst. If in doubt say No. Used |
| by the register allocator to do move coalescing. |
| */ |
| Bool isMove_TILEGXInstr ( TILEGXInstr * i, HReg * src, HReg * dst ) |
| { |
| /* Moves between integer regs */ |
| if (i->tag == GXin_Alu) { |
| // or Rd,Rs,Rs == mov Rd, Rs |
| if (i->GXin.Alu.op != GXalu_OR) |
| return False; |
| if (i->GXin.Alu.srcR->tag != GXrh_Reg) |
| return False; |
| if (!sameHReg(i->GXin.Alu.srcR->GXrh.Reg.reg, i->GXin.Alu.srcL)) |
| return False; |
| *src = i->GXin.Alu.srcL; |
| *dst = i->GXin.Alu.dst; |
| return True; |
| } |
| return False; |
| } |
| |
| /* Generate tilegx spill/reload instructions under the direction of the |
| register allocator. |
| */ |
| void genSpill_TILEGX ( /*OUT*/ HInstr ** i1, /*OUT*/ HInstr ** i2, HReg rreg, |
| Int offsetB ) |
| { |
| TILEGXAMode *am; |
| vassert(offsetB >= 0); |
| vassert(!hregIsVirtual(rreg)); |
| *i1 = *i2 = NULL; |
| am = TILEGXAMode_IR(offsetB, TILEGXGuestStatePointer()); |
| |
| switch (hregClass(rreg)) { |
| case HRcInt64: |
| *i1 = TILEGXInstr_Store(8, am, rreg); |
| break; |
| case HRcInt32: |
| *i1 = TILEGXInstr_Store(4, am, rreg); |
| break; |
| default: |
| ppHRegClass(hregClass(rreg)); |
| vpanic("genSpill_TILEGX: unimplemented regclass"); |
| } |
| } |
| |
| void genReload_TILEGX ( /*OUT*/ HInstr ** i1, /*OUT*/ HInstr ** i2, HReg rreg, |
| Int offsetB ) |
| { |
| TILEGXAMode *am; |
| vassert(!hregIsVirtual(rreg)); |
| am = TILEGXAMode_IR(offsetB, TILEGXGuestStatePointer()); |
| |
| switch (hregClass(rreg)) { |
| case HRcInt64: |
| *i1 = TILEGXInstr_Load(8, rreg, am); |
| break; |
| case HRcInt32: |
| *i1 = TILEGXInstr_Load(4, rreg, am); |
| break; |
| default: |
| ppHRegClass(hregClass(rreg)); |
| vpanic("genReload_TILEGX: unimplemented regclass"); |
| break; |
| } |
| } |
| |
| /* --------- The tilegx assembler --------- */ |
| |
| static UChar *mkInsnBin ( UChar * p, ULong insn ) |
| { |
| vassert(insn != (ULong)(-1)); |
| if (((Addr)p) & 7) { |
| vex_printf("p=%p\n", p); |
| vassert((((Addr)p) & 7) == 0); |
| } |
| *((ULong *)(Addr)p) = insn; |
| p += 8; |
| return p; |
| } |
| |
| static Int display_insn ( struct tilegx_decoded_instruction |
| decoded[1] ) |
| { |
| Int i; |
| for (i = 0; |
| decoded[i].opcode && (i < 1); |
| i++) { |
| Int n; |
| vex_printf("%s ", decoded[i].opcode->name); |
| |
| for (n = 0; n < decoded[i].opcode->num_operands; n++) { |
| const struct tilegx_operand *op = decoded[i].operands[n]; |
| |
| if (op->type == TILEGX_OP_TYPE_REGISTER) |
| vex_printf("r%d", (Int) decoded[i].operand_values[n]); |
| else |
| vex_printf("%llu", (ULong)decoded[i].operand_values[n]); |
| |
| if (n != (decoded[i].opcode->num_operands - 1)) |
| vex_printf(", "); |
| } |
| vex_printf(" "); |
| } |
| return i; |
| } |
| |
| |
| Int decode_and_display ( tilegx_bundle_bits *p, Int count, ULong pc ) |
| { |
| struct tilegx_decoded_instruction |
| decode[TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE]; |
| Int i; |
| |
| #ifdef TILEGX_DEBUG |
| vex_printf("Insn@0x%lx\n", (ULong)p); |
| #endif |
| |
| if (count > 0x1000) { |
| vex_printf("insn count: %d", count); |
| vassert(0); |
| } |
| |
| for (i = 0 ; i < count ; i++) { |
| if (pc) { |
| vex_printf("%012llx %016llx ", pc, (ULong)p[i]); |
| pc += 8; |
| } |
| parse_insn_tilegx(p[i], 0, decode); |
| |
| Int n, k, bundled = 0; |
| |
| for(k = 0; (k < TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE) && decode[k].opcode; |
| k++) { |
| if (decode[k].opcode->mnemonic != TILEGX_OPC_FNOP) |
| bundled++; |
| } |
| |
| /* Print "{", ";" and "}" only if multiple instructions are bundled. */ |
| if (bundled > 1) |
| vex_printf("{ "); |
| |
| n = bundled; |
| for(k = 0; (k < TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE) && decode[k].opcode; |
| k++) { |
| if (decode[k].opcode->mnemonic == TILEGX_OPC_FNOP) |
| continue; |
| |
| display_insn(&decode[k]); |
| |
| if (--n > 0) |
| vex_printf("; "); |
| } |
| |
| if (bundled > 1) |
| vex_printf(" }"); |
| |
| vex_printf("\n"); |
| } |
| return count; |
| } |
| |
| static UInt iregNo ( HReg r ) |
| { |
| UInt n; |
| vassert(hregClass(r) == HRcInt64); |
| vassert(!hregIsVirtual(r)); |
| n = hregEncoding(r); |
| vassert(n <= 63); |
| return n; |
| } |
| |
| static UChar *doAMode_IR ( UChar * p, UInt opc1, UInt rSD, TILEGXAMode * am ) |
| { |
| UInt rA; |
| vassert(am->tag == GXam_IR); |
| |
| rA = iregNo(am->GXam.IR.base); |
| |
| if (opc1 == TILEGX_OPC_ST1 || opc1 == TILEGX_OPC_ST2 || |
| opc1 == TILEGX_OPC_ST4 || opc1 == TILEGX_OPC_ST) { |
| if ( am->GXam.IR.index ) { |
| /* r51 is reserved scratch registers. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 51, rA, am->GXam.IR.index)); |
| /* store rSD to address in r51 */ |
| p = mkInsnBin(p, mkTileGxInsn(opc1, 2, 51, rSD)); |
| } else { |
| /* store rSD to address in rA */ |
| p = mkInsnBin(p, mkTileGxInsn(opc1, 2, rA, rSD)); |
| } |
| } else { |
| if ( am->GXam.IR.index ) { |
| /* r51 is reserved scratch registers. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 51, rA, am->GXam.IR.index)); |
| /* load from address in r51 to rSD. */ |
| p = mkInsnBin(p, mkTileGxInsn(opc1, 2, rSD, 51)); |
| } else { |
| /* load from address in rA to rSD. */ |
| p = mkInsnBin(p, mkTileGxInsn(opc1, 2, rSD, rA)); |
| } |
| } |
| return p; |
| } |
| |
| /* Generate a machine-word sized load or store using exact 2 bundles. |
| Simplified version of the GXin_Load and GXin_Store cases below. */ |
| static UChar* do_load_or_store_machine_word ( UChar* p, Bool isLoad, UInt reg, |
| TILEGXAMode* am ) |
| { |
| UInt rA = iregNo(am->GXam.IR.base); |
| |
| if (am->tag != GXam_IR) |
| vpanic(__func__); |
| |
| if (isLoad) /* load */ { |
| /* r51 is reserved scratch registers. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 51, rA, am->GXam.IR.index)); |
| /* load from address in r51 to rSD. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_LD, 2, reg, 51)); |
| } else /* store */ { |
| /* r51 is reserved scratch registers. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 51, rA, am->GXam.IR.index)); |
| /* store rSD to address in r51 */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ST, 2, 51, reg)); |
| } |
| return p; |
| } |
| |
| /* Load imm to r_dst */ |
| static UChar *mkLoadImm ( UChar * p, UInt r_dst, ULong imm ) |
| { |
| vassert(r_dst < 0x40); |
| |
| if (imm == 0) |
| { |
| /* A special case, use r63 - zero register. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MOVE, 2, r_dst, 63)); |
| } |
| else if (imm >= 0xFFFFFFFFFFFF8000ULL || imm < 0x8000) |
| { |
| /* only need one 16-bit sign-extendable movli instructon. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MOVELI, 2, |
| r_dst, imm & 0xFFFF)); |
| |
| } |
| else if (imm >= 0xFFFFFFFF80000000ULL || imm < 0x80000000ULL) |
| { |
| /* Sign-extendable moveli and a shl16insli */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MOVELI, 2, |
| r_dst, |
| (imm >> 16) & 0xFFFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL16INSLI, 3, |
| r_dst, r_dst, |
| (imm & 0xFFFF))); |
| |
| } |
| else |
| { |
| /* A general slower and rare case, use 4 instructions/bundles: |
| moveli r_dst, imm[63:48] |
| shl16insli r_dst, imm[47:32] |
| shl16insli r_dst, imm[31:16] |
| shl16insli r_dst, imm[15: 0] |
| */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MOVELI, 2, |
| r_dst, |
| (imm >> 48) & 0xFFFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL16INSLI, 3, |
| r_dst, r_dst, |
| (imm >> 32) & 0xFFFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL16INSLI, 3, |
| r_dst, r_dst, |
| (imm >> 16) & 0xFFFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL16INSLI, 3, |
| r_dst, r_dst, |
| imm & 0xFFFF)); |
| } |
| return p; |
| } |
| |
| /* Load imm to r_dst using exact 4 bundles. A special case of above |
| mkLoadImm(...). */ |
| static UChar *mkLoadImm_EXACTLY4 ( UChar * p, UInt r_dst, ULong imm ) |
| { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MOVELI, 2, |
| r_dst, |
| (imm >> 48) & 0xFFFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL16INSLI, 3, |
| r_dst, r_dst, |
| (imm >> 32) & 0xFFFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL16INSLI, 3, |
| r_dst, r_dst, |
| (imm >> 16) & 0xFFFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL16INSLI, 3, |
| r_dst, r_dst, |
| (imm) & 0xFFFF)); |
| return p; |
| } |
| |
| /* Move r_dst to r_src */ |
| static UChar *mkMoveReg ( UChar * p, UInt r_dst, UInt r_src ) |
| { |
| vassert(r_dst < 0x40); |
| vassert(r_src < 0x40); |
| |
| if (r_dst != r_src) { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MOVE, 2, |
| r_dst, r_src)); |
| } |
| return p; |
| } |
| |
| /* Emit an instruction into buf and return the number of bytes used. |
| Note that buf is not the insn's final place, and therefore it is |
| imperative to emit position-independent code. */ |
| Int emit_TILEGXInstr ( Bool* is_profInc, |
| UChar* buf, |
| Int nbuf, |
| TILEGXInstr* i, |
| Bool mode64, |
| VexEndness endness_host, |
| void* disp_cp_chain_me_to_slowEP, |
| void* disp_cp_chain_me_to_fastEP, |
| void* disp_cp_xindir, |
| void* disp_cp_xassisted ) |
| { |
| Int instr_bytes = 0; |
| UChar *p = &buf[0]; |
| UChar *ptmp = p; |
| vassert(nbuf >= 32); |
| vassert(!((Addr)p & 0x7)); |
| vassert (mode64); |
| |
| switch (i->tag) { |
| case GXin_MovCond: { |
| |
| TILEGXRH *srcR = i->GXin.MovCond.srcR; |
| UInt condR = iregNo(i->GXin.MovCond.condR); |
| UInt dst = iregNo(i->GXin.MovCond.dst); |
| |
| UInt srcL = iregNo(i->GXin.MovCond.srcL); |
| |
| if (i->GXin.MovCond.cond == TILEGXcc_EZ) { |
| if (srcR->tag == GXrh_Reg) { |
| p = mkMoveReg(p, dst, iregNo(srcR->GXrh.Reg.reg)); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMOVEQZ, 3, |
| dst, condR, srcL)); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MOVELI, 2, |
| dst, srcR->GXrh.Imm.imm16)); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMOVEQZ, 3, |
| dst, condR, srcL)); |
| } |
| } else { |
| vassert(0); |
| } |
| |
| goto done; |
| } |
| case GXin_LI: |
| |
| // Tilegx, load literal |
| p = mkLoadImm(p, iregNo(i->GXin.LI.dst), i->GXin.LI.imm); |
| goto done; |
| |
| case GXin_Alu: { |
| TILEGXRH *srcR = i->GXin.Alu.srcR; |
| Bool immR = toBool(srcR->tag == GXrh_Imm); |
| UInt r_dst = iregNo(i->GXin.Alu.dst); |
| UInt r_srcL = iregNo(i->GXin.Alu.srcL); |
| UInt r_srcR = immR ? (-1) /*bogus */ : iregNo(srcR->GXrh.Reg.reg); |
| |
| switch (i->GXin.Alu.op) { |
| /*GXalu_ADD, GXalu_SUB, GXalu_AND, GXalu_OR, GXalu_NOR, GXalu_XOR */ |
| case GXalu_ADD: |
| if (immR) { |
| vassert(srcR->GXrh.Imm.imm16 != 0x8000); |
| if (srcR->GXrh.Imm.syned) |
| /* addi */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| else |
| /* addiu, use shil16insli for tilegx */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL16INSLI, 3, |
| r_dst, 63, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| /* addu */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADD, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| break; |
| case GXalu_SUB: |
| if (immR) { |
| /* addi , but with negated imm */ |
| vassert(srcR->GXrh.Imm.syned); |
| vassert(srcR->GXrh.Imm.imm16 != 0x8000); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| r_dst, r_srcL, |
| -srcR->GXrh.Imm.imm16)); |
| } else { |
| /* subu */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SUB, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| break; |
| case GXalu_AND: |
| if (immR) { |
| /* andi */ |
| vassert((srcR->GXrh.Imm.imm16 >> 8 == 0) || |
| (srcR->GXrh.Imm.imm16 >> 8 == 0xFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ANDI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| |
| } else { |
| /* and */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_AND, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| break; |
| case GXalu_OR: |
| if (immR) { |
| /* ori */ |
| vassert((srcR->GXrh.Imm.imm16 >> 8 == 0) || |
| (srcR->GXrh.Imm.imm16 >> 8 == 0xFF)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ORI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| /* or */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_OR, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| break; |
| case GXalu_NOR: |
| /* nor */ |
| vassert(!immR); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_NOR, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| break; |
| case GXalu_XOR: |
| if (immR) { |
| /* xori */ |
| vassert(srcR->GXrh.Imm.syned); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_XORI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| /* xor */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_XOR, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| break; |
| |
| default: |
| goto bad; |
| } |
| goto done; |
| } |
| |
| case GXin_Shft: { |
| TILEGXRH *srcR = i->GXin.Shft.srcR; |
| Bool sz32 = i->GXin.Shft.sz32; |
| Bool immR = toBool(srcR->tag == GXrh_Imm); |
| UInt r_dst = iregNo(i->GXin.Shft.dst); |
| UInt r_srcL = iregNo(i->GXin.Shft.srcL); |
| UInt r_srcR = immR ? (-1) /*bogus */ : iregNo(srcR->GXrh.Reg.reg); |
| |
| switch (i->GXin.Shft.op) { |
| case GXshft_SLL: |
| if (sz32) { |
| if (immR) { |
| UInt n = srcR->GXrh.Imm.imm16; |
| vassert(n >= 0 && n < 64); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHLXI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| /* shift variable */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHLX, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| } else { |
| if (immR) { |
| UInt n = srcR->GXrh.Imm.imm16; |
| vassert(n >= 0 && n < 64); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHLI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHL, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| } |
| break; |
| |
| case GXshft_SLL8x8: |
| if (immR) { |
| UInt n = srcR->GXrh.Imm.imm16; |
| vassert(n >= 0 && n < 64); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_V1SHLI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_V1SHL, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| break; |
| |
| case GXshft_SRL8x8: |
| if (immR) { |
| UInt n = srcR->GXrh.Imm.imm16; |
| vassert(n >= 0 && n < 64); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_V1SHRUI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_V1SHRU, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| break; |
| |
| case GXshft_SRL: |
| if (sz32) { |
| // SRL, SRLV |
| if (immR) { |
| UInt n = srcR->GXrh.Imm.imm16; |
| vassert(n >= 0 && n < 32); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHRUXI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| /* shift variable */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHRUX, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| } else { |
| // DSRL, DSRL32, DSRLV |
| if (immR) { |
| UInt n = srcR->GXrh.Imm.imm16; |
| vassert((n >= 0 && n < 64)); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHRUI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHRU, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| } |
| break; |
| |
| case GXshft_SRA: |
| if (sz32) { |
| // SRA, SRAV |
| if (immR) { |
| UInt n = srcR->GXrh.Imm.imm16; |
| vassert(n >= 0 && n < 64); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHRSI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| |
| } else { |
| /* shift variable */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHRS, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| } else { |
| // DSRA, DSRA32, DSRAV |
| if (immR) { |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHRSI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_SHRS, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| } |
| break; |
| |
| default: |
| goto bad; |
| } |
| |
| goto done; |
| } |
| |
| case GXin_Unary: { |
| UInt r_dst = iregNo(i->GXin.Unary.dst); |
| UInt r_src = iregNo(i->GXin.Unary.src); |
| |
| switch (i->GXin.Unary.op) { |
| /* GXun_CLZ, GXun_NOP */ |
| case GXun_CLZ: //clz |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CLZ, 2, |
| r_dst, r_src)); |
| break; |
| case GXun_CTZ: //ctz |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CTZ, 2, |
| r_dst, r_src)); |
| break; |
| |
| case GXun_NOP: |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_NOP, 0)); |
| break; |
| } |
| goto done; |
| } |
| |
| case GXin_Cmp: { |
| |
| Bool syned = i->GXin.Cmp.syned; |
| UInt r_srcL = iregNo(i->GXin.Cmp.srcL); |
| UInt r_srcR = iregNo(i->GXin.Cmp.srcR); |
| UInt r_dst = iregNo(i->GXin.Cmp.dst); |
| |
| switch (i->GXin.Cmp.cond) { |
| case TILEGXcc_EQ: |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPEQ, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| |
| break; |
| |
| case TILEGXcc_NE: |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPNE, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| |
| break; |
| case TILEGXcc_LT: |
| /* slt r_dst, r_srcL, r_srcR */ |
| |
| if (syned) |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPLTS, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| else |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPLTU, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| |
| break; |
| case TILEGXcc_LO: |
| /* sltu r_dst, r_srcL, r_srcR */ |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPLTU, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| |
| break; |
| case TILEGXcc_LE: |
| if (syned) |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPLES, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| else |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPLEU, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| break; |
| case TILEGXcc_LS: |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPLTU, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| break; |
| default: |
| goto bad; |
| } |
| goto done; |
| } |
| |
| case GXin_CmpI: { |
| |
| TILEGXRH *srcR = i->GXin.CmpI.srcR; |
| Bool immR = toBool(srcR->tag == GXrh_Imm); |
| UInt r_dst = iregNo(i->GXin.CmpI.dst); |
| UInt r_srcL = iregNo(i->GXin.CmpI.srcL); |
| UInt r_srcR = immR ? (-1) /*bogus */ : iregNo(srcR->GXrh.Reg.reg); |
| |
| switch (i->GXin.CmpI.cond) { |
| case TILEGXcc_EQ8x8: |
| if (immR) { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_V1CMPEQI, 3, |
| r_dst, r_srcL, |
| srcR->GXrh.Imm.imm16)); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_V1CMPEQ, 3, |
| r_dst, r_srcL, |
| r_srcR)); |
| } |
| break; |
| |
| case TILEGXcc_NE8x8: |
| if (immR) { |
| vassert(0); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_V1CMPNE, 3, |
| r_dst, r_srcR, |
| r_srcL)); |
| } |
| break; |
| default: |
| vassert(0); |
| } |
| goto done; |
| break; |
| } |
| |
| case GXin_Bf: { |
| |
| /* Bit Field */ |
| UInt r_dst = iregNo(i->GXin.Bf.dst); |
| UInt r_src = iregNo(i->GXin.Bf.src); |
| UInt Start = i->GXin.Bf.Start; |
| UInt End = i->GXin.Bf.End; |
| |
| switch (i->GXin.Bf.op) { |
| case GXbf_EXTS: |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_BFEXTS, 4, |
| r_dst, r_src, |
| Start, End)); |
| |
| break; |
| case GXbf_EXTU: |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_BFEXTU, 4, |
| r_dst, r_src, |
| Start, End)); |
| |
| break; |
| case GXbf_INS: |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_BFINS, 4, |
| r_dst, r_src, |
| Start, End)); |
| |
| break; |
| default: |
| vassert(0); |
| } |
| goto done; |
| break; |
| } |
| |
| case GXin_Acas: { |
| |
| /* Atomic */ |
| UInt sz = i->GXin.Acas.sz; |
| UInt old = iregNo(i->GXin.Acas.old); |
| UInt addr= iregNo(i->GXin.Acas.addr); |
| UInt new = iregNo(i->GXin.Acas.new); |
| |
| switch (i->GXin.Acas.op) { |
| case GXacas_CMPEXCH: |
| { |
| UInt exp = iregNo(i->GXin.Acas.exp); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MTSPR, 2, |
| 0x2780, exp)); |
| if (sz == 8) |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPEXCH, 3, |
| old, addr, new)); |
| else |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_CMPEXCH4, 3, |
| old, addr, new)); |
| } |
| break; |
| |
| case GXacas_EXCH: |
| if (sz == 8) |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_EXCH, 3, |
| old, addr, new)); |
| else |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_EXCH4, 3, |
| old, addr, new)); |
| break; |
| |
| case GXacas_FetchAnd: |
| if (sz == 8) |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_FETCHAND, 3, |
| old, addr, new)); |
| else |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_FETCHAND4, 3, |
| old, addr, new)); |
| break; |
| |
| case GXacas_FetchAdd: |
| if (sz == 8) |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_FETCHADD, 3, |
| old, addr, new)); |
| else |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_FETCHADD4, 3, |
| old, addr, new)); |
| break; |
| |
| case GXacas_FetchAddgez: |
| if (sz == 8) |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_FETCHADDGEZ, 3, |
| old, addr, new)); |
| else |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_FETCHADDGEZ4, 3, |
| old, addr, new)); |
| break; |
| |
| case GXacas_FetchOr: |
| if (sz == 8) |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_FETCHOR, 3, |
| old, addr, new)); |
| else |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_FETCHOR4, 3, |
| old, addr, new)); |
| break; |
| |
| default: vassert(0); |
| } |
| goto done; |
| break; |
| } |
| |
| case GXin_Mul: { |
| |
| /* Multiplication */ |
| Bool syned = i->GXin.Mul.syned; |
| Bool widening = i->GXin.Mul.widening; |
| Bool sz32 = i->GXin.Mul.sz32; |
| UInt r_srcL = iregNo(i->GXin.Mul.srcL); |
| UInt r_srcR = iregNo(i->GXin.Mul.srcR); |
| UInt r_dst = iregNo(i->GXin.Mul.dst); |
| |
| vassert(widening); // always widen. |
| vassert(!sz32); // always be 64 bits. |
| |
| if (syned) { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MUL_LS_LS, 3, |
| r_dst, r_srcL, r_srcR)); |
| } else { |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MUL_LU_LU, 3, |
| r_dst, r_srcL, r_srcR)); |
| } |
| goto done; |
| } |
| |
| case GXin_Call: { |
| |
| /* Function Call. */ |
| TILEGXCondCode cond = i->GXin.Call.cond; |
| UInt r_dst = 11; /* using r11 as address temporary */ |
| |
| /* jump over the following insns if conditional. */ |
| if (cond != TILEGXcc_AL) { |
| /* jmp fwds if !condition */ |
| /* don't know how many bytes to jump over yet... |
| make space for a jump instruction + nop!!! and fill in later. */ |
| ptmp = p; /* fill in this bit later */ |
| p += 8; |
| } |
| |
| /* load target to r_dst */ |
| p = mkLoadImm(p, r_dst, i->GXin.Call.target); |
| |
| /* jalr %r_dst */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_JALRP, 1, |
| r_dst)); |
| |
| /* Fix up the conditional jump, if there was one. */ |
| if (cond != TILEGXcc_AL) { |
| UInt r_src = iregNo(i->GXin.Call.src); |
| Int delta = p - ptmp; |
| |
| vassert(cond == TILEGXcc_EQ); |
| |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_BEQZ, 2, |
| r_src, delta / 8)); |
| } |
| goto done; |
| } |
| |
| case GXin_XDirect: { |
| /* NB: what goes on here has to be very closely coordinated |
| with the chainXDirect_TILEGX and unchainXDirect_TILEGX below. */ |
| /* We're generating chain-me requests here, so we need to be |
| sure this is actually allowed -- no-redir translations |
| can't use chain-me's. Hence: */ |
| vassert(disp_cp_chain_me_to_slowEP != NULL); |
| vassert(disp_cp_chain_me_to_fastEP != NULL); |
| |
| /* Use ptmp for backpatching conditional jumps. */ |
| ptmp = NULL; |
| |
| /* First, if this is conditional, create a conditional |
| jump over the rest of it. Or at least, leave a space for |
| it that we will shortly fill in. */ |
| if (i->GXin.XDirect.cond != TILEGXcc_AL) { |
| vassert(i->GXin.XDirect.cond != TILEGXcc_NV); |
| ptmp = p; |
| p += 24; |
| } |
| |
| /* Update the guest PC. */ |
| /* move r11, dstGA */ |
| /* st amPC, r11 */ |
| p = mkLoadImm_EXACTLY4(p, /*r*/ 11, (ULong)i->GXin.XDirect.dstGA); |
| |
| p = do_load_or_store_machine_word(p, False /*!isLoad*/ , /*r*/ 11, |
| i->GXin.XDirect.amPC); |
| |
| /* --- FIRST PATCHABLE BYTE follows --- */ |
| /* VG_(disp_cp_chain_me_to_{slowEP,fastEP}) (where we're |
| calling to) backs up the return address, so as to find the |
| address of the first patchable byte. So: don't change the |
| number of instructions (3) below. */ |
| /* move r9, VG_(disp_cp_chain_me_to_{slowEP,fastEP}) */ |
| /* jr r11 */ |
| void* disp_cp_chain_me |
| = i->GXin.XDirect.toFastEP ? disp_cp_chain_me_to_fastEP |
| : disp_cp_chain_me_to_slowEP; |
| p = mkLoadImm_EXACTLY4(p, /*r*/ 11, |
| (Addr)disp_cp_chain_me); |
| /* jalr r11 */ |
| /* nop */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_JALR, 1, 11)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_NOP, 0)); |
| |
| /* --- END of PATCHABLE BYTES --- */ |
| |
| /* Fix up the conditional jump, if there was one. */ |
| if (i->GXin.XDirect.cond != TILEGXcc_AL) { |
| Int delta = p - ptmp; |
| delta = delta / 8 - 3; |
| |
| /* ld r11, COND_OFFSET(GuestSP=r50) |
| beqz r11, delta |
| */ |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 11, 50, COND_OFFSET())); |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_LD, 2, |
| 11, 11)); |
| |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_BEQZ, 2, |
| 11, delta)); |
| |
| } |
| goto done; |
| } |
| |
| case GXin_XIndir: { |
| /* We're generating transfers that could lead indirectly to a |
| chain-me, so we need to be sure this is actually allowed -- |
| no-redir translations are not allowed to reach normal |
| translations without going through the scheduler. That means |
| no XDirects or XIndirs out from no-redir translations. |
| Hence: */ |
| vassert(disp_cp_xindir != NULL); |
| |
| /* Use ptmp for backpatching conditional jumps. */ |
| ptmp = NULL; |
| |
| /* First off, if this is conditional, create a conditional |
| jump over the rest of it. */ |
| if (i->GXin.XIndir.cond != TILEGXcc_AL) { |
| vassert(i->GXin.XIndir.cond != TILEGXcc_NV); |
| ptmp = p; |
| p += 24; |
| } |
| |
| /* Update the guest PC. */ |
| /* st amPC, dstGA */ |
| p = do_load_or_store_machine_word(p, False /*!isLoad*/ , |
| iregNo(i->GXin.XIndir.dstGA), |
| i->GXin.XIndir.amPC); |
| |
| /* move r11, VG_(disp_cp_xindir), 4 bundles. */ |
| /* jalr r11 */ |
| /* nop */ |
| p = mkLoadImm_EXACTLY4(p, /*r*/ 11, |
| (Addr)disp_cp_xindir); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_JALR, 1, 11)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_NOP, 0)); |
| |
| /* Fix up the conditional jump, if there was one. */ |
| if (i->GXin.XIndir.cond != TILEGXcc_AL) { |
| Int delta = p - ptmp; |
| delta = delta / 8 - 3; |
| vassert(delta > 0 && delta < 40); |
| |
| /* ld r11, COND_OFFSET($GuestSP) |
| beqz r11, delta */ |
| |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 11, 50, COND_OFFSET())); |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_LD, 2, |
| 11, 11)); |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_BEQZ, 2, |
| 11, delta)); |
| } |
| goto done; |
| } |
| |
| case GXin_XAssisted: { |
| /* First off, if this is conditional, create a conditional jump |
| over the rest of it. Or at least, leave a space for it that |
| we will shortly fill in. */ |
| ptmp = NULL; |
| if (i->GXin.XAssisted.cond != TILEGXcc_AL) { |
| vassert(i->GXin.XAssisted.cond != TILEGXcc_NV); |
| ptmp = p; |
| p += 24; |
| } |
| |
| /* Update the guest PC. */ |
| /* st amPC, dstGA */ |
| p = do_load_or_store_machine_word(p, False /*!isLoad*/ , |
| iregNo(i->GXin.XIndir.dstGA), |
| i->GXin.XIndir.amPC); |
| |
| UInt trcval = 0; |
| switch (i->GXin.XAssisted.jk) { |
| case Ijk_ClientReq: trcval = VEX_TRC_JMP_CLIENTREQ; break; |
| case Ijk_Sys_syscall: trcval = VEX_TRC_JMP_SYS_SYSCALL; break; |
| case Ijk_Yield: trcval = VEX_TRC_JMP_YIELD; break; |
| case Ijk_EmWarn: trcval = VEX_TRC_JMP_EMWARN; break; |
| case Ijk_EmFail: trcval = VEX_TRC_JMP_EMFAIL; break; |
| case Ijk_NoDecode: trcval = VEX_TRC_JMP_NODECODE; break; |
| case Ijk_InvalICache: trcval = VEX_TRC_JMP_INVALICACHE; break; |
| case Ijk_NoRedir: trcval = VEX_TRC_JMP_NOREDIR; break; |
| case Ijk_SigILL: trcval = VEX_TRC_JMP_SIGILL; break; |
| case Ijk_SigTRAP: trcval = VEX_TRC_JMP_SIGTRAP; break; |
| case Ijk_SigBUS: trcval = VEX_TRC_JMP_SIGBUS; break; |
| case Ijk_SigFPE_IntDiv: trcval = VEX_TRC_JMP_SIGFPE_INTDIV; break; |
| case Ijk_SigFPE_IntOvf: trcval = VEX_TRC_JMP_SIGFPE_INTOVF; break; |
| case Ijk_Boring: trcval = VEX_TRC_JMP_BORING; break; |
| case Ijk_Ret: |
| { |
| /* Tilegx "iret" instruction. */ |
| trcval = VEX_TRC_JMP_BORING; |
| /* Interrupt return "iret", setup the jump address into EX_CONTRXT_0_0. |
| Read context_0_1 from guest_state */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 51, 50, OFFSET_EX1)); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_LD, 2, |
| 11, 51)); |
| /* Write into host cpu's context_0_1 spr. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MTSPR, 2, |
| 0x2581, 11)); |
| /* Read context_0_0 from guest_state */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 51, 50, OFFSET_EX0)); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_LD, 2, |
| 11, 51)); |
| /* Write into host cpu's context_0_0 spr */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_MTSPR, 2, |
| 0x2580, 11)); |
| /* Update the guest PC so branch to the iret target address |
| in EX_CONTEXT_0. */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 51, 50, 512)); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ST, 2, |
| 51, 11)); |
| } |
| break; |
| /* We don't expect to see the following being assisted. |
| case Ijk_Call: |
| fallthrough */ |
| default: |
| ppIRJumpKind(i->GXin.XAssisted.jk); |
| vpanic("emit_TILEGXInstr.GXin_XAssisted: unexpected jump kind"); |
| } |
| vassert(trcval != 0); |
| |
| /* moveli r50, trcval */ |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, 50, 63, trcval)); |
| |
| /* move r11, VG_(disp_cp_xassisted) */ |
| |
| p = mkLoadImm_EXACTLY4(p, /*r*/ 11, |
| (Addr)disp_cp_xassisted); |
| /* jalr r11 |
| nop */ |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_JALR, 1, 11)); |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_NOP, 0)); |
| |
| /* Fix up the conditional jump, if there was one. */ |
| if (i->GXin.XAssisted.cond != TILEGXcc_AL) { |
| Int delta = p - ptmp; |
| delta = delta / 8 - 3; |
| vassert(delta > 0 && delta < 40); |
| |
| /* ld r11, COND_OFFSET($GuestSP) |
| beqz r11, delta |
| nop */ |
| |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_ADDLI, 3, |
| 11, 50, COND_OFFSET())); |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_LD, 2, |
| 11, 11)); |
| ptmp = mkInsnBin(ptmp, mkTileGxInsn(TILEGX_OPC_BEQZ, 2, |
| 11, delta)); |
| } |
| goto done; |
| } |
| |
| case GXin_EvCheck: { |
| /* We generate: |
| ld r11, amCounter |
| addi r11, r11, -1 |
| st amCounter, r11 |
| bgez r11, nofail |
| ld r11, amFailAddr |
| jalr r11 |
| nop |
| nofail: |
| */ |
| UChar* p0 = p; |
| /* ld r11, amCounter */ |
| p = do_load_or_store_machine_word(p, True /*isLoad*/ , /*r*/ 11, |
| i->GXin.EvCheck.amCounter); |
| |
| /* addi r11,r11,-1 */ |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDI, 3, |
| 11, 11, -1)); |
| |
| /* st amCounter, 11 */ |
| p = do_load_or_store_machine_word(p, False /*!isLoad*/ , /*r*/ 11, |
| i->GXin.EvCheck.amCounter); |
| |
| /* Reserve a bundle, fill it after the do_load_or_store_machine_word. |
| since we are not sure how many bundles it takes. */ |
| UChar* p1 = p; |
| p += 8; |
| /* bgez t9, nofail */ |
| |
| /* lw/ld r9, amFailAddr */ |
| p = do_load_or_store_machine_word(p, True /*isLoad*/ , /*r*/ 11, |
| i->GXin.EvCheck.amFailAddr); |
| |
| mkInsnBin(p1, mkTileGxInsn(TILEGX_OPC_BGEZ, 2, |
| 11, 2 + (p - p1) / 8)); |
| |
| /* jalr r11 */ |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_JALR, 1, 11)); |
| |
| /* nop */ |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_NOP, 0)); |
| |
| /* nofail: */ |
| |
| /* Crosscheck */ |
| vassert(evCheckSzB_TILEGX() == (UChar*)p - (UChar*)p0); |
| goto done; |
| } |
| |
| case GXin_ProfInc: { |
| /* Generate a code template to increment a memory location whose |
| address will be known later as an immediate value. This code |
| template will be patched once the memory location is known. |
| For now we do this with address == 0x65556555. */ |
| /* 64-bit: |
| move r11, 0x6555655565556555ULL |
| ld r51, r11 |
| addi r51, r51, 1 |
| st r11, r51 |
| */ |
| |
| /* move r11, 0x6555655565556555ULL */ |
| p = mkLoadImm_EXACTLY4(p, /*r*/ 11, 0x6555655565556555ULL); |
| |
| /* ld r51, r11 */ |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_LD, 2, 51, 11)); |
| |
| /* addi r51, r51, 1 */ |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ADDI, 3, 51, 51, 1)); |
| |
| /* st r11, r51 */ |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_ST, 2, 11, 51)); |
| |
| /* Tell the caller .. */ |
| vassert(!(*is_profInc)); |
| *is_profInc = True; |
| goto done; |
| } |
| |
| case GXin_Load: { |
| TILEGXAMode *am_addr = i->GXin.Load.src; |
| if (am_addr->tag == GXam_IR) { |
| UInt r_dst = iregNo(i->GXin.Load.dst); |
| UInt opc, sz = i->GXin.Load.sz; |
| if ((sz == 4 || sz == 8)) { |
| /* should be guaranteed to us by iselWordExpr_AMode */ |
| vassert(0 == (am_addr->GXam.IR.index & 3)); |
| } |
| |
| // Note: Valgrind memory load has no sign-extend. We extend explicitly. |
| switch (sz) { |
| case 1: |
| opc = TILEGX_OPC_LD1U; |
| break; |
| case 2: |
| opc = TILEGX_OPC_LD2U; |
| break; |
| case 4: |
| opc = TILEGX_OPC_LD4U; |
| break; |
| case 8: |
| opc = TILEGX_OPC_LD; |
| break; |
| default: |
| goto bad; |
| } |
| |
| p = doAMode_IR(p, opc, r_dst, am_addr); |
| goto done; |
| |
| } |
| } |
| |
| case GXin_Store: { |
| TILEGXAMode *am_addr = i->GXin.Store.dst; |
| if (am_addr->tag == GXam_IR) { |
| UInt r_src = iregNo(i->GXin.Store.src); |
| UInt opc, sz = i->GXin.Store.sz; |
| switch (sz) { |
| case 1: |
| opc = TILEGX_OPC_ST1; |
| break; |
| case 2: |
| opc = TILEGX_OPC_ST2; |
| break; |
| case 4: |
| opc = TILEGX_OPC_ST4; |
| break; |
| case 8: |
| opc = TILEGX_OPC_ST; |
| break; |
| default: |
| goto bad; |
| } |
| |
| p = doAMode_IR(p, opc, r_src, am_addr); |
| goto done; |
| } else { |
| vassert(0); |
| } |
| } |
| |
| case GXin_RdWrLR: { |
| UInt reg = iregNo(i->GXin.RdWrLR.gpr); |
| Bool wrLR = i->GXin.RdWrLR.wrLR; |
| if (wrLR) |
| p = mkMoveReg(p, 55, reg); |
| else |
| p = mkMoveReg(p, reg, 55); |
| goto done; |
| } |
| |
| default: |
| goto bad; |
| } |
| |
| bad: |
| vex_printf("\n=> "); |
| vpanic("emit_TILEGXInstr"); |
| /*NOTREACHED*/ |
| |
| done: |
| instr_bytes = p - &buf[0]; |
| /* Instr byte count must be modular of 8. */ |
| vassert(0 == (instr_bytes & 0x7)); |
| |
| if ( 0) { |
| Int k; |
| for (k = 0; k < instr_bytes; k += 8) |
| decode_and_display((ULong *)(Addr)&buf[k], 1, 0); |
| } |
| |
| /* Limit the JIT size. */ |
| vassert(instr_bytes <= 256); |
| return instr_bytes; |
| } |
| |
| |
| Int evCheckSzB_TILEGX ( void ) |
| { |
| UInt kInstrSize = 8; |
| return 10*kInstrSize; |
| } |
| |
| VexInvalRange chainXDirect_TILEGX ( VexEndness endness_host, |
| void* place_to_chain, |
| const void* disp_cp_chain_me_EXPECTED, |
| const void* place_to_jump_to, |
| Bool mode64 ) |
| { |
| vassert(mode64); |
| vassert(endness_host == VexEndnessLE); |
| /* What we're expecting to see is: |
| move r11, disp_cp_chain_me_to_EXPECTED |
| jalr r11 |
| nop |
| viz |
| <32 bytes generated by mkLoadImm_EXACTLY4> |
| jalr r11 |
| nop |
| */ |
| UChar* p = (UChar*)place_to_chain; |
| vassert(0 == (7 & (HWord)p)); |
| |
| #ifdef TILEGX_DEBUG |
| vex_printf("chainXDirect_TILEGX: disp_cp_chain_me_EXPECTED=%p\n", |
| disp_cp_chain_me_EXPECTED); |
| decode_and_display(p, 6, p); |
| |
| vex_printf("chainXDirect_TILEGX: place_to_jump_to=%p\n", |
| place_to_jump_to); |
| #endif |
| |
| /* And what we want to change it to is either: |
| move r11, place_to_jump_to |
| jalr r11 |
| nop |
| viz |
| <32 bytes generated by mkLoadImm_EXACTLY4> |
| jalr r11 |
| nop |
| |
| The replacement has the same length as the original. |
| */ |
| |
| p = mkLoadImm_EXACTLY4(p, /*r*/ 11, |
| (Addr)place_to_jump_to); |
| |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_JALR, 1, 11)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_NOP, 0)); |
| |
| #ifdef TILEGX_DEBUG |
| decode_and_display((UChar*)place_to_chain, 8, place_to_chain); |
| #endif |
| |
| Int len = p - (UChar*)place_to_chain; |
| vassert(len == 48); /* stay sane */ |
| VexInvalRange vir = {(HWord)place_to_chain, len}; |
| return vir; |
| } |
| |
| VexInvalRange unchainXDirect_TILEGX ( VexEndness endness_host, |
| void* place_to_unchain, |
| const void* place_to_jump_to_EXPECTED, |
| const void* disp_cp_chain_me, |
| Bool mode64 ) |
| { |
| vassert(mode64); |
| vassert(endness_host == VexEndnessLE); |
| /* What we're expecting to see is: |
| move r11, place_to_jump_to_EXPECTED |
| jalr r11 |
| nop |
| viz |
| <32 bytes generated by mkLoadImm_EXACTLY4> |
| jalr r11 |
| nop |
| */ |
| UChar* p = (UChar*)place_to_unchain; |
| vassert(0 == (7 & (HWord)p)); |
| |
| /* And what we want to change it to is: |
| move r11, disp_cp_chain_me |
| jalr r11 |
| nop |
| viz |
| <32 bytes generated by mkLoadImm_EXACTLY4> |
| jalr r11 |
| nop |
| The replacement has the same length as the original. |
| */ |
| p = mkLoadImm_EXACTLY4(p, /*r*/ 11, |
| (Addr)disp_cp_chain_me); |
| |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_JALR, 1, 11)); |
| |
| p = mkInsnBin(p, mkTileGxInsn(TILEGX_OPC_NOP, 0)); |
| |
| Int len = p - (UChar*)place_to_unchain; |
| vassert(len == 48); /* stay sane */ |
| VexInvalRange vir = {(HWord)place_to_unchain, len}; |
| return vir; |
| } |
| |
| /* Patch the counter address into a profile inc point, as previously |
| created by the GXin_ProfInc case for emit_TILEGXInstr. */ |
| VexInvalRange patchProfInc_TILEGX ( VexEndness endness_host, |
| void* place_to_patch, |
| const ULong* location_of_counter, |
| Bool mode64 ) |
| { |
| vassert(mode64); |
| vassert(endness_host == VexEndnessLE); |
| UChar* p = (UChar*)place_to_patch; |
| vassert(0 == (7 & (HWord)p)); |
| |
| p = mkLoadImm_EXACTLY4(p, /*r*/ 11, |
| (Addr)location_of_counter); |
| |
| VexInvalRange vir = {(HWord)p, 32}; |
| return vir; |
| } |
| |
| /*---------------------------------------------------------------*/ |
| /*--- end host_tilegx_defs.c ---*/ |
| /*---------------------------------------------------------------*/ |