2 * Copyright (c) 1993, 1994 Barton P. Miller, Jeff Hollingsworth,
3 * Bruce Irvin, Jon Cargille, Krishna Kunchithapadam, Karen
4 * Karavanic, Tia Newhall, Mark Callaghan. All rights reserved.
6 * This software is furnished under the condition that it may not be
7 * provided or otherwise made available to, or used by, any other
8 * person, except as provided for by the terms of applicable license
9 * agreements. No title to or ownership of the software is hereby
10 * transferred. The name of the principals may not be used in any
11 * advertising or publicity related to this software without specific,
12 * written prior authorization. Any use of this software must include
13 * the above copyright notice.
18 static char Copyright[] = "@(#) Copyright (c) 1993, 1994 Barton P. Miller, \
19 Jeff Hollingsworth, Jon Cargille, Krishna Kunchithapadam, Karen Karavanic,\
20 Tia Newhall, Mark Callaghan. All rights reserved.";
22 static char rcsid[] = "@(#) $Header: /home/jaw/CVSROOT_20081103/CVSROOT/core/dyninstAPI/src/Attic/inst-hppa.C,v 1.4 1995/12/21 16:43:09 naim Exp $";
26 * inst-hppa.C - Identify instrumentation points for PA-RISC processors.
28 * $Log: inst-hppa.C,v $
29 * Revision 1.4 1995/12/21 16:43:09 naim
30 * Implementing rel operators <,<=,>,>= for the hp - naim
32 * Revision 1.3 1995/12/19 01:04:46 hollings
33 * Moved the implementation of registerSpace::readOnlyRegister to processor
34 * specific files (since it is).
35 * Fixed a bug in Power relOps cases.
37 * Revision 1.2 1995/11/30 15:13:40 krisna
38 * added call to matherr in main.C
39 * added code templates for callOp in inst-hppa.C
41 * Revision 1.1 1995/11/29 18:47:46 krisna
42 * hpux/hppa instrumentation code
46 #include "util/h/headers.h"
48 #include "rtinst/h/rtinst.h"
53 #include "inst-hppa.h"
54 #include "arch-hppa.h"
57 #include "internalMetrics.h"
60 #include "showerror.h"
62 #define perror(a) P_abort();
64 #define ABS(x) ((x) > 0 ? x : -x)
65 #define MAX_BRANCH ((0x1<<18)+8) /* 17-signed bits, lshift by 2, +8 */
67 unsigned getMaxBranch() {
71 const char *registerNames[] =
72 { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
73 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
74 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
75 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" };
77 dictionary_hash<string, unsigned> funcFrequencyTable(string::hash);
79 /* not called at all, why is this here
80 string getStrOp(int op)
83 case ADDop: return("add");
84 case ANDop: return("and");
85 case BLop: return("bl");
86 case COMBTop: return("combt");
87 case LDILop: return("ldil");
88 case LDOop: return("ldo");
89 case LDWop: return("ldw");
90 case ORop: return("or");
91 case STWop: return("stw");
92 case SUBop: return("sub");
94 default: return("???");
99 inline int w_w1_w2_to_offset(unsigned w, unsigned w1, unsigned w2) {
101 x.raw = (w == 0) ? (0) : (~0); // sign extend the offset
107 return ((x.raw << 2) + 8);
110 inline assemble_w_w1_w2 offset_to_w_w1_w2(int offset) {
111 if (ABS(offset) > MAX_BRANCH) {
112 logLine("a branch too far\n");
113 showErrorCallback(52, "");
117 offset -= 8; /* 8 always added to branch */
118 offset >>= 2; /* undo lshift */
120 assemble_w_w1_w2 ret;
125 inline void generateBranchInsn(instruction *insn, int offset, reg lr = 0)
127 assemble_w_w1_w2 x = offset_to_w_w1_w2(offset);
131 insn->bi.r2_p_b_t = lr;
132 insn->bi.r1_im5_w1_x = x.w_w1_w2.w1;
133 insn->bi.c_s_ext3 = BLext;
134 insn->bi.w1_w2 = x.w_w1_w2.w2;
136 insn->bi.w = x.w_w1_w2.w;
138 // logLine("ba,a %x\n", offset);
141 inline void generateCompareBranch(instruction* insn, reg rs1, reg rs2,
143 offset -= 8; /* 8 always added to branch */
144 offset >>= 2; /* undo the lshift of assembly */
145 unsigned w = (offset % 2); /* pull out the w1,w bits */
146 unsigned w1 = offset >> 1;
149 insn->bi.op = COMBTop;
150 insn->bi.r2_p_b_t = rs2;
151 insn->bi.r1_im5_w1_x = rs1;
152 insn->bi.c_s_ext3 = COMCLR_EQ_C;
158 inline void genArithLogInsn(instruction *insn, unsigned op, unsigned ext7,
159 reg rs1, reg rs2, reg rd)
163 insn->ci.al.r2 = rs2;
164 insn->ci.al.r1 = rs1;
165 insn->ci.al.ext7 = ext7;
168 // logLine("%s %%%s,%%%s,%%%s\n", getStrOp(op), registerNames[rs1],
169 // registerNames[rs2], registerNames[rd]);
172 inline void generateNOOP(instruction *insn)
175 genArithLogInsn(insn, ORop, ORext7, 0, 0, 0);
180 inline void genCmpOp(instruction *insn, unsigned cond, unsigned flow,
181 reg rs1, reg rs2, reg rd)
184 insn->ci.al.op = COMCLRop;
185 insn->ci.al.r2 = rs2;
186 insn->ci.al.r1 = rs1;
187 insn->ci.al.c = cond;
188 insn->ci.al.f = flow;
189 insn->ci.al.ext7 = COMCLRext7;
193 inline void generateLoadConst(instruction *insn, int src1, int dest,
195 const unsigned MAX_IMM21 = 0x1fffff;
196 if (ABS(src1) > MAX_IMM21) {
198 insn->li.op = LDILop;
200 insn->li.im21 = ((src1 >> 11) & 0x1fffff);
204 insn->mr.ls.op = LDOop;
205 insn->mr.ls.b = dest;
206 insn->mr.ls.tr = dest;
208 insn->mr.ls.im14 = src1 & 0x7ff;
210 base += 2*sizeof(instruction);
214 insn->mr.ls.op = LDOop;
216 insn->mr.ls.tr = dest;
218 insn->mr.ls.im14 = src1 & 0x7ff;
220 base += sizeof(instruction);
224 inline void generateLoad(instruction *insn, reg src, reg dest)
227 insn->mr.ls.op = LDWop;
229 insn->mr.ls.tr = dest;
231 insn->mr.ls.im14 = 0;
234 inline void generateStore(instruction *insn, int rd, int rs1, int im14 = 0)
237 insn->mr.ls.op = STWop;
241 insn->mr.ls.im14 = im14;
244 instPoint::instPoint(pdFunction *f, const instruction &instr,
245 const image *owner, Address adr,
247 : addr(adr), originalInstruction(instr), inDelaySlot(false), isDelayed(false),
248 callIndirect(false), callAggregate(false), callee(NULL), func(f)
250 // why is the following code not in power
252 delaySlotInsn.raw = owner->get_instruction(adr+4);
253 aggregateInsn.raw = owner->get_instruction(adr+8);
255 if (IS_DELAYED_INST(instr))
258 if (owner->isValidAddress(adr-4)) {
260 iplus1.raw = owner->get_instruction(adr-4);
261 if (IS_DELAYED_INST(iplus1) && !delayOK) {
262 // ostrstream os(errorLine, 1024, ios::out);
263 // os << "** inst point " << func->file->fullName << "/"
264 // << func->prettyName() << " at addr " << addr <<
265 // " in a delay slot\n";
266 // logLine(errorLine);
272 // Determine if the called function is a "library" function or a "user" function
273 // This cannot be done until all of the functions have been seen, verified, and
276 void pdFunction::checkCallPoints() {
281 vector<instPoint*> non_lib;
283 for (i=0; i<calls.size(); ++i) {
284 /* check to see where we are calling */
288 if (!isCallInsn(p->originalInstruction)) {
289 logLine("what is a non-call (jump) doing in set of calls\n");
293 if (isInsnType(p->originalInstruction, CALLmask, CALLmatch)) {
295 int offset = w_w1_w2_to_offset(p->originalInstruction.bi.w,
296 p->originalInstruction.bi.r1_im5_w1_x,
297 p->originalInstruction.bi.w1_w2);
298 loc_addr = p->addr + offset;
299 pdFunction *pdf = (file_->exec())->findFunction(loc_addr);
300 if (pdf && !pdf->isLibTag()) {
307 // Indirect call -- be conservative, assume it is a call to
308 // an unnamed user function
309 assert(!p->callee); assert(p->callIndirect);
317 // TODO we cannot find the called function by address at this point in time
318 // because the called function may not have been seen.
320 Address pdFunction::newCallPoint(const Address adr, const instruction instr,
321 const image *owner, bool &err)
327 point = new instPoint(this, instr, owner, adr, false);
329 if (!isCallInsn(instr)) {
330 logLine("what is a non-call (jump) doing in newCallPoint\n");
334 if (!isInsnType(instr, CALLmask, CALLmatch)) {
335 point->callIndirect = true;
336 point->callee = NULL;
338 point->callIndirect = false;
340 point->callAggregate = false;
347 void initDefaultPointFrequencyTable()
349 funcFrequencyTable[EXIT_NAME] = 1;
356 funcFrequencyTable["main"] = 1;
357 funcFrequencyTable["DYNINSTsampleValues"] = 1;
359 fp = fopen("freq.input", "r");
363 printf("found freq.input file\n");
366 fscanf(fp, "%s %f\n", name, &value);
367 funcFrequencyTable[name] = (int) value;
368 printf("adding %s %f\n", name, value);
375 * Get an etimate of the frequency for the passed instPoint.
376 * This is not (always) the same as the function that contains the point.
378 * The function is selected as follows:
380 * If the point is an entry or an exit return the function name.
381 * If the point is a call and the callee can be determined, return the called
383 * else return the funcation containing the point.
385 * WARNING: This code contins arbitray values for func frequency (both user
386 * and system). This should be refined over time.
388 * Using 1000 calls sec to be one SD from the mean for most FPSPEC apps.
392 float getPointFrequency(instPoint *point)
398 func = point->callee;
402 if (!funcFrequencyTable.defines(func->prettyName())) {
403 if (func->isLibTag()) {
409 return (funcFrequencyTable[func->prettyName()]);
414 // return cost in cycles of executing at this point. This is the cost
415 // of the base tramp if it is the first at this point or 0 otherwise.
417 int getPointCost(process *proc, instPoint *point)
419 if (proc->baseMap.defines(point)) {
422 // 8 cycles for base tramp
428 * Given and instruction, relocate it to a new address, patching up
429 * any relative addressing that is present.
431 * There are 6 types of unconditional branches
432 * and 8 types of conditional branches on the PA-RISC.
434 * Most of the unconditional branches are base/index relative
435 * and cannot (need not?) be relocated. However, most of the
436 * conditional branches can be relocated since they are PC relative.
438 * The simple solution is that for the instrumentation points we
439 * can handle (procedure entry, procedure exit, and procedure
440 * call), only certain types of branches can occur. If anything
441 * else occurs in these places, the compiler is screwed.
444 void relocateInstruction(instruction *insn, int origAddr, int targetAddr)
448 // make sure that GATE, BE, BLE are not allowed
449 if ((isInsnType(*insn, GATEmask, GATEmatch))
450 || (isInsnType(*insn, BEmask, BEmatch))
451 || (isInsnType(*insn, BLEmask, BLEmatch))) {
452 logLine("one of GATE, BE, BLE instruction called\n");
455 else if (isInsnType(*insn, CALLmask, CALLmatch)) {
456 int oldOffset = w_w1_w2_to_offset(insn->bi.w,
457 insn->bi.r1_im5_w1_x,
459 newOffset = origAddr - targetAddr + oldOffset;
460 assemble_w_w1_w2 x = offset_to_w_w1_w2(newOffset);
461 insn->bi.w = x.w_w1_w2.w;
462 insn->bi.r1_im5_w1_x = x.w_w1_w2.w1;
463 insn->bi.w1_w2 = x.w_w1_w2.w2;
466 /* The rest of the instructions should be fine as is */
469 trampTemplate baseTemplate;
471 extern "C" void baseTramp();
473 void initATramp(trampTemplate *thisTemp, instruction *tramp)
477 // TODO - are these offset always positive?
478 thisTemp->trampTemp = (void *) tramp;
479 for (temp = tramp; temp->raw != END_TRAMP; temp++) {
481 case LOCAL_PRE_BRANCH:
482 thisTemp->localPreOffset = ((void*)temp - (void*)tramp);
484 case GLOBAL_PRE_BRANCH:
485 thisTemp->globalPreOffset = ((void*)temp - (void*)tramp);
487 case LOCAL_POST_BRANCH:
488 thisTemp->localPostOffset = ((void*)temp - (void*)tramp);
490 case GLOBAL_POST_BRANCH:
491 thisTemp->globalPostOffset = ((void*)temp - (void*)tramp);
495 thisTemp->size = (int) temp - (int) tramp;
498 registerSpace *regSpace;
500 // return values come via r28, r29; these should be dead at call point
501 int deadRegList[] = { 28, 29 };
503 // r26, r25, r24, r23 are call arguments (in that order)
504 int liveRegList[] = { 1, 19, 20, 21, 22, 23, 24, 31, 26, 25 };
505 // all are caller save registers
509 static bool inited=false;
514 initATramp(&baseTemplate, (instruction *) baseTramp);
516 regSpace = new registerSpace(sizeof(deadRegList)/sizeof(int), deadRegList,
517 sizeof(liveRegList)/sizeof(int), liveRegList);
521 * Install a base tramp -- fill calls with nop's for now.
524 void installBaseTramp(unsigned baseAddr,
532 code = new instruction[baseTemplate.size];
533 memcpy((char *) code, (char*) baseTemplate.trampTemp, baseTemplate.size);
534 // bcopy(baseTemplate.trampTemp, code, baseTemplate.size);
536 for (temp = code, currAddr = baseAddr;
537 (currAddr - baseAddr) < baseTemplate.size;
538 temp++, currAddr += sizeof(instruction)) {
539 if (temp->raw == EMULATE_INSN) {
540 *temp = location->originalInstruction;
541 relocateInstruction(temp, location->addr, currAddr);
542 if (location->isDelayed) {
543 /* copy delay slot instruction into tramp instance */
544 *(temp+1) = location->delaySlotInsn;
546 } else if (temp->raw == RETURN_INSN) {
547 int disp = location->addr + sizeof(instruction) - currAddr;
548 if (location->isDelayed) {
549 disp += sizeof(instruction);
551 generateBranchInsn(temp, disp);
552 } else if ((temp->raw == LOCAL_PRE_BRANCH) ||
553 (temp->raw == GLOBAL_PRE_BRANCH) ||
554 (temp->raw == LOCAL_POST_BRANCH) ||
555 (temp->raw == GLOBAL_POST_BRANCH)) {
556 /* fill with no-op */
561 proc->writeDataSpace((caddr_t)baseAddr, baseTemplate.size, (caddr_t) code);
566 void generateNoOp(process *proc, int addr)
571 proc->writeTextWord((caddr_t)addr, insn.raw);
575 unsigned findAndInstallBaseTramp(process *proc,
582 if (!globalProc->baseMap.defines(location)) {
583 ret = inferiorMalloc(globalProc, baseTemplate.size, textHeap);
584 installBaseTramp(ret, location, globalProc);
585 generateBranch(globalProc, location->addr, (int) ret);
586 globalProc->baseMap[location] = ret;
588 ret = globalProc->baseMap[location];
595 * Install a single tramp.
598 void installTramp(instInstance *inst, char *code, int codeSize)
601 insnGenerated += codeSize/sizeof(int);
604 (inst->proc)->writeDataSpace((caddr_t)inst->trampBase, codeSize, code);
608 * change the insn at addr to be a branch to newAddr.
609 * Used to add multiple tramps to a point.
611 void generateBranch(process *proc, unsigned fromAddr, unsigned newAddr)
616 disp = newAddr-fromAddr;
617 generateBranchInsn(&insn, disp);
620 proc->writeTextWord((caddr_t)fromAddr, insn.raw);
623 int callsTrackedFuncP(instPoint *point)
625 if (point->callIndirect) {
627 // TODO this won't compile now
628 // it's rare to call a library function as a parameter.
629 sprintf(errorLine, "*** Warning call indirect\n from %s %s (addr %d)\n",
630 point->func->file->fullName, point->func->prettyName, point->addr);
635 if (point->callee && !(point->callee->isLibTag())) {
644 * return the function asociated with a point.
646 * If the point is a funcation call, and we know the function being called,
647 * then we use that. Otherwise it is the function that contains the
650 * This is done to return a better idea of which function we are using.
652 pdFunction *getFunction(instPoint *point)
654 return(point->callee ? point->callee : point->func);
657 unsigned emit(opCode op, reg src1, reg src2, reg dest, char *i, unsigned &base)
660 instruction *insn = (instruction *) ((void*)&i[base]);
662 if (op == loadConstOp) {
663 generateLoadConst(insn, src1, dest, base);
664 } else if (op == loadOp) {
665 generateLoadConst(insn, src1, dest, base);
666 insn = (instruction *) ((void*)&i[base]);
668 generateLoad(insn, dest, dest);
669 base += sizeof(instruction);
670 } else if (op == storeOp) {
671 generateLoadConst(insn, dest, src2, base);
672 insn = (instruction *) ((void*)&i[base]);
674 generateStore(insn, src1, dest);
675 base += sizeof(instruction);
676 } else if (op == ifOp) {
677 generateCompareBranch(insn, src1, 0, dest); insn++;
679 base += sizeof(instruction)*2;
680 return(base - sizeof(instruction)); // whatever this means??
681 } else if (op == callOp) {
682 // printf("callOp: src1=%d, src2=%d, dest=%d\n", src1, src2, dest);
683 logLine("cannot generate callOp for now\n");
684 return(28); // return register is %r28
687 // the following assumes that no one has screwed around
688 // with the stack. if not, chaos.
690 // stw %r2, -20(0,%r30) ; save RP
691 // stw %r26, -??(0,%r30) ; save r26 (but where)
692 // stw %r25, -??(0,%r30) ; save r25 (but where)
693 // stw %r22, -??(0,%r30) ; save r22 (but where)
695 generateStore(insn, 2, 30, -20); insn++;
696 base += sizeof(instruction);
698 // set up %r26 and %r25, arguments
700 genArithLogInsn(insn, ORop, ORext7, src1, 0, 26); insn++;
701 base += sizeof(instruction);
704 genArithLogInsn(insn, ORop, ORext7, src2, 0, 25); insn++;
705 base += sizeof(instruction);
708 // need absolute location of current instruction
709 // to compute offsets
711 // int offset = dest - currentAddress;
712 // generateBranchLinkInsn(insn, offset, 2); insn++;
713 // generateNOOP(insn); insn++;
714 // base += 2*sizeof(instruction);
716 // return value is the register with the return value from the
717 // function. i.e. %r28
720 } else if (op == trampPreamble) {
721 // generate code to update observed cost, but we have no cost model
723 } else if (op == trampTrailer) {
724 // restore any saved registers, but we never save them
725 // dest is in words of offset and generateBranchInsn is bytes offset
726 generateBranchInsn(insn, dest << 2);
727 base += sizeof(instruction);
732 base += sizeof(instruction);
734 return(base - 2 * sizeof(instruction));
736 } else if (op == noOp) {
738 base += sizeof(instruction);
740 } else if (op == getParamOp) {
741 // first 4 params are in reg r23, r24, r25, r26
746 } else if (op == saveRegOp) {
747 // should never be called for this platform.
777 genCmpOp(insn, COMCLR_EQ_C, COMCLR_EQ_F, src1, src2, dest);
778 base += sizeof(instruction);
783 genCmpOp(insn, COMCLR_NE_C, COMCLR_NE_F, src1, src2, dest);
784 base += sizeof(instruction);
789 genCmpOp(insn, COMCLR_LT_C, COMCLR_LT_F, src1, src2, dest);
790 base += sizeof(instruction);
795 genCmpOp(insn, COMCLR_LE_C, COMCLR_LE_T, src1, src2, dest);
796 base += sizeof(instruction);
801 genCmpOp(insn, COMCLR_LE_C, COMCLR_LE_F, src1, src2, dest);
802 base += sizeof(instruction);
807 genCmpOp(insn, COMCLR_LT_C, COMCLR_LT_T, src1, src2, dest);
808 base += sizeof(instruction);
813 // emulated via "floating point!" multiply
815 // emulated via millicode
823 genArithLogInsn(insn, aop, ext7, src1, src2, dest);
824 base += sizeof(instruction);
830 // someone needs to work out the cost model for pa-risc
832 int getInsnCost(opCode op)
834 return 0; // simple cost model
837 bool isReturnInsn(const instruction instr)
839 // all returns are of the form
842 // inter-space jumps/returns are ignored
843 if (isInsnType(instr, RETmask, RETmatch)) {
850 // The exact semantics of the heap are processor specific.
852 // find all DYNINST symbols that are data symbols
854 bool image::heapIsOk(const vector<sym_data> &find_us) {
855 Address curr, instHeapEnd;
859 for (unsigned i=0; i<find_us.size(); i++) {
860 str = find_us[i].name;
861 if (!linkedFile.get_symbol(str, sym)) {
862 string str1 = string("_") + str.string_of();
863 if (!linkedFile.get_symbol(str1, sym) && find_us[i].must_find) {
865 msg = string("Cannot find ") + str + string(". Exiting");
866 statusLine(msg.string_of());
867 showErrorCallback(50, msg);
871 addInternalSymbol(str, sym.addr());
874 string ghb = GLOBAL_HEAP_BASE;
875 if (!linkedFile.get_symbol(ghb, sym)) {
876 ghb = U_GLOBAL_HEAP_BASE;
877 if (!linkedFile.get_symbol(ghb, sym)) {
879 msg = string("Cannot find ") + str + string(". Exiting");
880 statusLine(msg.string_of());
881 showErrorCallback(50, msg);
885 instHeapEnd = sym.addr();
886 addInternalSymbol(ghb, instHeapEnd);
887 ghb = INFERIOR_HEAP_BASE;
889 if (!linkedFile.get_symbol(ghb, sym)) {
890 ghb = UINFERIOR_HEAP_BASE;
891 if (!linkedFile.get_symbol(ghb, sym)) {
893 msg = string("Cannot find ") + str + string(". Cannot use this application");
894 statusLine(msg.string_of());
895 showErrorCallback(50, msg);
900 addInternalSymbol(ghb, curr);
901 if (curr > instHeapEnd) instHeapEnd = curr;
903 // check that we can get to our heap.
904 if (instHeapEnd > getMaxBranch()) {
905 logLine("*** FATAL ERROR: Program text + data too big for dyninst\n");
906 sprintf(errorLine, " heap ends at %x\n", instHeapEnd);
909 } else if (instHeapEnd + SYN_INST_BUF_SIZE > getMaxBranch()) {
910 logLine("WARNING: Program text + data could be too big for dyninst\n");
911 showErrorCallback(54, "");
918 // This is specific to some processors that have info in registers that we
919 // can read, but should not write.
920 // HPPA has no such registers.
922 bool registerSpace::readOnlyRegister(reg reg_number) {