DIRECTORY RS6000Architecture USING [RS6000Instruction], BreakWorldArchitecture USING [Address], Rope USING [ROPE]; RS6000Manger: CEDAR DEFINITIONS ~ { Manger: TYPE ~ MACHINE DEPENDENT RECORD [ variants: SELECT COMPUTED MangerVariant FROM normal => [ sheep: RS6000Architecture.RS6000Instruction, normalContinue: RS6000Architecture.RS6000Instruction, normalNoop: RS6000Architecture.RS6000Instruction, tag: RS6000Architecture.RS6000Instruction], branch => [ dcti: RS6000Architecture.RS6000Instruction, branchDelay: RS6000Architecture.RS6000Instruction, branchContinue: RS6000Architecture.RS6000Instruction, tag: RS6000Architecture.RS6000Instruction], call => [ callAddrHi: RS6000Architecture.RS6000Instruction, callJmp: RS6000Architecture.RS6000Instruction, callNoop: RS6000Architecture.RS6000Instruction, tag: RS6000Architecture.RS6000Instruction], jmplO7 => [ jmplO7Jmp: RS6000Architecture.RS6000Instruction, jmplO7Noop: RS6000Architecture.RS6000Instruction, jmplO7Pad: RS6000Architecture.RS6000Instruction, tag: RS6000Architecture.RS6000Instruction], jmplG0 => [ jmplG0Jmp: RS6000Architecture.RS6000Instruction, jmplG0Delay: RS6000Architecture.RS6000Instruction, jmplG0Pad: RS6000Architecture.RS6000Instruction, tag: RS6000Architecture.RS6000Instruction], ENDCASE ]; MangerVariant: TYPE ~ { noneOfTheAbove, normal, branch, call, jmplO7, jmplG0 }; NormalManger: TYPE ~ Manger.normal; BranchManger: TYPE ~ Manger.branch; CallManger: TYPE ~ Manger.call; JmplO7Manger: TYPE ~ Manger.jmplO7; JmplG0Manger: TYPE ~ Manger.jmplG0; Install: PROCEDURE [ address: BreakWorldArchitecture.Address, manger: BreakWorldArchitecture.Address, patchCode: BreakWorldArchitecture.Address] RETURNS []; Uninstall: PROCEDURE [ address: BreakWorldArchitecture.Address, manger: BreakWorldArchitecture.Address, patchCode: BreakWorldArchitecture.Address] RETURNS []; ErrorCode: TYPE ~ ATOM _ nullErrorCode; nullErrorCode: ErrorCode ~ NIL; ErrorMessage: TYPE ~ Rope.ROPE _ nullErrorMessage; nullErrorMessage: ErrorMessage ~ NIL; CantInstall: ERROR [code: ErrorCode, message: ErrorMessage]; CantUninstall: ERROR [code: ErrorCode, message: ErrorMessage]; }. & RS6000Manger.mesa Copyright 1990 by Xerox Corporation. All rights reserved. Peter B. Kessler, April 2, 1990 12:03 pm PDT Udagawa, August 2, 1991 7:13 pm PDT Mangers come in (for now) five flavors: normal for ordinary instructions (non-delayed control transfers), branch for simple delayed control transfers, call for call instructions, jmplO7 for jmpl's that use %o7 as their destination (e.g. indirect calls). jmplG0 for jmpl's that use %g0 as their destination (e.g. returns). The tag for which kind of manger it is is kept in a ``sethi %lo(tag), %g0'' instruction in the last instruction of the manger. The tag is used to figure out how to put the original instruction back when the breakpoint is removed. Normal instruction stream: becomes: normal manger: sheep ba,a patch sheep continue: continue: ba,a continue -- This is the easy case, we just jump to the patch to exeucte the instruction. Branch instruction stream: becomes: branch manger: bicc target ba,a patch bicc target delaySlot delaySlot delaySlot continue: continue: ba,a continue -- With a simple delay slot, we just jump to a copy (relocated) of the instruction and its delay slot. If the branch falls through, we just transfer back to the main code stream. Call instruction stream: becomes: call manger: call target call patch sethi %hi(target), %g1 delaySlot delaySlot jmpl %g1+%lo(target), %g0 noop -- The trick here is to call the patch, thus setting up the return address because the callee probably uses it (e.g. to return, or return aggregate results, etc.). We allow the instruction in the delay slot to execute (what if it messes with the return address? So be it, that's what it wanted to do.). In the manger, we move the target address to %g1 (which I think is free at this point), and then jmpl to the callee without setting the return address. JmplO7 instruction stream: becomes: jmplO7 manger: jmpl target, %o7 call patch jmpl target, %g0 delaySlot delaySlot noop -- Again, the trick here is to call the patch, thus setting up the return address because the callee probably uses it (e.g. to return, or return aggregate results, etc.). We allow the instruction in the delay slot to execute. In the manger, we jmpl to the callee without setting the return address. JmplG0 instruction stream: becomes: jmplG0 manger: jmpl target, %g0 ba,a patch jmpl target, %g0 delaySlot delaySlot delaySlot -- ``jmpl target, %g0'' has a standard abbreviation of just ``jmp target'', and there are standard abbreviations of ``ret'' and ``retl'' for ``jmp %i7+8'' and ``jmp %o7+8''. -- This is just a computed delayed transfer. We branch to a copy of the instruction and it's delay slot, as in the branch instruction case, above, excpet this isn't a conditional branch. Types Manger variants are ``computed'' by examining the ``tag'' field, which is an executable noop (because it gets executed in the call case) that encodes which variant we have. In a better world, we would just waste a word after the header to store the tag and other data about the patch. There's still some hair to reconstructing the original instruction in the call case, but life is hard. no delay slot has simple delay slot has delay slot, and have to get return address pointed to breakpoint address, since callee computes return point. Use a call to get to the patch, and set up for a ``jmpl %g1,%g0'' from the patch to the target of the original call. (%g1 is free since we are calling a procedure.) has delay slot, and have to get return address pointed to breakpoint address, since callee computes return point. Can only do these when link register is %o7, since we use a call to get to the patch, since we don't have the instruction space to point a register at the patch. has delay slot, and is an unconditional branch. Procedures Errors ]NewlineDelimiter codeKKKZ"