| // -*- mode:c++ -*- |
| |
| // Copyright (c) 2009 The University of Edinburgh |
| // Copyright (c) 2021 IBM Corporation |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer; |
| // redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution; |
| // neither the name of the copyright holders nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| //////////////////////////////////////////////////////////////////// |
| // |
| // Control transfer instructions |
| // |
| // From the Power ISA Book I v3.0B, page 35, the following rules should |
| // be obeyed by programmers: |
| // |
| // - Use branch instructions where LK == 1 only as subroutine calls. |
| // - Pair each subroutine call with a bclr instruction with BH == 00 |
| // and LK == 0 that returns from the subroutine. |
| // - Do not use bclr with LK == 1 as a subroutine call. |
| // |
| // Therefore, all versions that update the link register (LR) are flagged |
| // as calls, except bclr with LK == 1 (i.e. bclrl). The latter should not |
| // be tagged as a return either. |
| |
| let {{ |
| |
| # Simple code to update link register (LR). |
| updateLrCode = 'LR = CIA + 4;' |
| |
| }}; |
| |
| |
| // Instructions that unconditionally branch either to an address relative |
| // to the current PC or an absolute address. |
| def format BranchOp(code, code_aa1, inst_flags = []) {{ |
| inst_flags += ('IsUncondControl', 'IsDirectControl') |
| |
| # Setup the 4 code versions and add code to update LR if necessary |
| code_lk1 = code + updateLrCode |
| code_aa1_lk1 = code_aa1 + updateLrCode |
| |
| # Generate the classes |
| (header_output, decoder_output, decode_block, exec_output) = \ |
| GenAluOp(name, Name, 'BranchOp', code, inst_flags, |
| CheckAaLkDecode, BasicConstructor) |
| (header_output_aa1, decoder_output_aa1, _, exec_output_aa1) = \ |
| GenAluOp(name, Name + 'AaSet', 'BranchOp', code_aa1, inst_flags, |
| CheckAaLkDecode, BasicConstructor) |
| (header_output_lk1, decoder_output_lk1, _, exec_output_lk1) = \ |
| GenAluOp(name, Name + 'LkSet', 'BranchOp', code_lk1, inst_flags, |
| CheckAaLkDecode, BasicConstructor) |
| (header_output_aa1_lk1, decoder_output_aa1_lk1, _, exec_output_aa1_lk1) = \ |
| GenAluOp(name, Name + 'AaSetLkSet', 'BranchOp', code_aa1_lk1, |
| inst_flags, CheckAaLkDecode, BasicConstructor) |
| |
| # Finally, add to the other outputs |
| header_output += \ |
| header_output_aa1 + header_output_lk1 + header_output_aa1_lk1 |
| decoder_output += \ |
| decoder_output_aa1 + decoder_output_lk1 + decoder_output_aa1_lk1 |
| exec_output += \ |
| exec_output_aa1 + exec_output_lk1 + exec_output_aa1_lk1 |
| }}; |
| |
| |
| let {{ |
| |
| # Check the condition register (CR) allows the branch to be taken. |
| def GetCondCode(br_code): |
| cond_code = 'Msr msr = MSR;\n' |
| cond_code += 'if (condOk(CR)) {\n' |
| cond_code += ' ' + br_code + '\n' |
| cond_code += '}\n' |
| cond_code += 'NIA = msr.sf ? NIA : NIA & UINT32_MAX;\n' |
| return cond_code |
| |
| # Check the condition register (CR) and count register (CTR) allow the |
| # branch to be taken. Also, in certain situations, decrement the count |
| # register too. This takes place in ctrOk within BranchCondOp classes. |
| def GetCtrCondCode(br_code): |
| cond_code = 'uint64_t ctr = CTR;\n' |
| cond_code += 'Msr msr = MSR;\n' |
| cond_code += 'if (ctrOk(ctr) && condOk(CR)) {\n' |
| cond_code += ' ' + br_code + '\n' |
| cond_code += '}\n' |
| cond_code += 'NIA = msr.sf ? NIA : NIA & UINT32_MAX;\n' |
| cond_code += 'CTR = ctr;\n' |
| return cond_code |
| |
| }}; |
| |
| |
| // Instructions that conditionally branch either to an address relative |
| // to the current PC or an absolute address depending on the value of the |
| // condition register (CR) and count register (CTR). |
| def format BranchDispCondOp(code, code_aa1, inst_flags = []) {{ |
| inst_flags += ('IsCondControl', 'IsDirectControl') |
| |
| # Setup the 4 code versions and add code to update LR if necessary |
| code = GetCtrCondCode(code) |
| code_aa1 = GetCtrCondCode(code_aa1) |
| code_lk1 = code + updateLrCode |
| code_aa1_lk1 = code_aa1 + updateLrCode |
| inst_flags_lk1 = inst_flags + [ 'IsCall' ] |
| |
| # Generate the classes |
| (header_output, decoder_output, decode_block, exec_output) = \ |
| GenAluOp(name, Name, 'BranchDispCondOp', code, inst_flags, |
| CheckAaLkDecode, BasicConstructor) |
| (header_output_aa1, decoder_output_aa1, _, exec_output_aa1) = \ |
| GenAluOp(name, Name + 'AaSet', 'BranchDispCondOp', code_aa1, |
| inst_flags, CheckAaLkDecode, BasicConstructor) |
| (header_output_lk1, decoder_output_lk1, _, exec_output_lk1) = \ |
| GenAluOp(name, Name + 'LkSet', 'BranchDispCondOp', code_lk1, |
| inst_flags_lk1, CheckAaLkDecode, BasicConstructor) |
| (header_output_aa1_lk1, decoder_output_aa1_lk1, _, exec_output_aa1_lk1) = \ |
| GenAluOp(name, Name + 'AaSetLkSet', 'BranchDispCondOp', code_aa1_lk1, |
| inst_flags_lk1, CheckAaLkDecode, BasicConstructor) |
| |
| # Finally, add to the other outputs |
| header_output += \ |
| header_output_aa1 + header_output_lk1 + header_output_aa1_lk1 |
| decoder_output += \ |
| decoder_output_aa1 + decoder_output_lk1 + decoder_output_aa1_lk1 |
| exec_output += \ |
| exec_output_aa1 + exec_output_lk1 + exec_output_aa1_lk1 |
| }}; |
| |
| |
| // Instructions that conditionally branch to an address in a register |
| // depending on the value of the condition register (CR) and count |
| // register (CTR). |
| def format BranchRegCondOp(code, checkCTR = 0, inst_flags = []) {{ |
| inst_flags += ('IsCondControl', 'IsIndirectControl') |
| |
| # Setup the 2 code versions and add code to update LR if necessary |
| if checkCTR: |
| code = GetCtrCondCode(code) |
| decode_template = CheckLkDecode |
| else: |
| code = GetCondCode(code) |
| decode_template = CheckBoLkDecode |
| code_lk1 = code + updateLrCode |
| inst_flags_lk1 = inst_flags + [ 'IsCall' ] |
| |
| # When LK is set, this cannot be used to return to the callee |
| if 'IsReturn' in inst_flags: |
| inst_flags_lk1.remove('IsReturn') |
| |
| # Generate the classes |
| (header_output, decoder_output, decode_block, exec_output) = \ |
| GenAluOp(name, Name, 'BranchRegCondOp', code, inst_flags, |
| decode_template, BasicConstructor) |
| (header_output_lk1, decoder_output_lk1, _, exec_output_lk1) = \ |
| GenAluOp(name, Name + 'LkSet', 'BranchRegCondOp', code_lk1, |
| inst_flags_lk1, decode_template, BasicConstructor) |
| |
| # Finally, add to the other outputs |
| header_output += header_output_lk1 |
| decoder_output += decoder_output_lk1 |
| exec_output += exec_output_lk1 |
| }}; |