| # Copyright (c) 2014, 2016, 2018-2019 ARM Limited |
| # All rights reserved |
| # |
| # The license below extends only to copyright in the software and shall |
| # not be construed as granting a license to any other intellectual |
| # property including but not limited to intellectual property relating |
| # to a hardware implementation of the functionality of the software |
| # licensed hereunder. You may use the software subject to the license |
| # terms below provided that you ensure that this notice is replicated |
| # unmodified and in its entirety in all distributions of the software, |
| # modified or unmodified, in source code or in binary form. |
| # |
| # Copyright (c) 2003-2005 The Regents of The University of Michigan |
| # Copyright (c) 2013,2015 Advanced Micro Devices, Inc. |
| # 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. |
| |
| from .util import assignRE, commentRE, stringRE |
| from .util import error |
| |
| class OperandList(object): |
| '''Find all the operands in the given code block. Returns an operand |
| descriptor list (instance of class OperandList).''' |
| def __init__(self, parser, code): |
| self.items = [] |
| self.bases = {} |
| # delete strings and comments so we don't match on operands inside |
| for regEx in (stringRE, commentRE): |
| code = regEx.sub('', code) |
| |
| # search for operands |
| for match in parser.operandsRE().finditer(code): |
| op = match.groups() |
| # regexp groups are operand full name, base, and extension |
| (op_full, op_base, op_ext) = op |
| # If is a elem operand, define or update the corresponding |
| # vector operand |
| isElem = False |
| if op_base in parser.elemToVector: |
| isElem = True |
| elem_op = (op_base, op_ext) |
| op_base = parser.elemToVector[op_base] |
| op_ext = '' # use the default one |
| # if the token following the operand is an assignment, this is |
| # a destination (LHS), else it's a source (RHS) |
| is_dest = (assignRE.match(code, match.end()) != None) |
| is_src = not is_dest |
| |
| # see if we've already seen this one |
| op_desc = self.find_base(op_base) |
| if op_desc: |
| if op_ext and op_ext != '' and op_desc.ext != op_ext: |
| error ('Inconsistent extensions for operand %s: %s - %s' \ |
| % (op_base, op_desc.ext, op_ext)) |
| op_desc.is_src = op_desc.is_src or is_src |
| op_desc.is_dest = op_desc.is_dest or is_dest |
| if isElem: |
| (elem_base, elem_ext) = elem_op |
| found = False |
| for ae in op_desc.active_elems: |
| (ae_base, ae_ext) = ae |
| if ae_base == elem_base: |
| if ae_ext != elem_ext: |
| error('Inconsistent extensions for elem' |
| ' operand %s' % elem_base) |
| else: |
| found = True |
| if not found: |
| op_desc.active_elems.append(elem_op) |
| else: |
| # new operand: create new descriptor |
| op_desc = parser.operandNameMap[op_base](parser, |
| op_full, op_ext, is_src, is_dest) |
| # if operand is a vector elem, add the corresponding vector |
| # operand if not already done |
| if isElem: |
| op_desc.elemExt = elem_op[1] |
| op_desc.active_elems = [elem_op] |
| self.append(op_desc) |
| |
| self.sort() |
| |
| # enumerate source & dest register operands... used in building |
| # constructor later |
| regs = list(filter(lambda i: i.isReg(), self.items)) |
| mem = list(filter(lambda i: i.isMem(), self.items)) |
| srcs = list(filter(lambda r: r.is_src, regs)) |
| dests = list(filter(lambda r: r.is_dest, regs)) |
| |
| for idx, reg in enumerate(srcs): |
| reg.src_reg_idx = idx |
| for idx, reg in enumerate(dests): |
| reg.dest_reg_idx = idx |
| |
| self.numSrcRegs = len(srcs) |
| self.numDestRegs = len(dests) |
| self.numFPDestRegs = sum(r.isFloatReg() for r in dests) |
| self.numIntDestRegs = sum(r.isIntReg() for r in dests) |
| self.numVecDestRegs = sum(r.isVecReg() for r in dests) |
| self.numVecPredDestRegs = sum(r.isVecPredReg() for r in dests) |
| self.numCCDestRegs = sum(r.isCCReg() for r in dests) |
| self.numMiscDestRegs = sum(r.isControlReg() for r in dests) |
| |
| if len(mem) > 1: |
| error("Code block has more than one memory operand") |
| |
| self.memOperand = mem[0] if mem else None |
| |
| # Flags to keep track if one or more operands are to be read/written |
| # conditionally. |
| self.predRead = any(i.hasReadPred() for i in self.items) |
| self.predWrite = any(i.hasWritePred() for i in self.items) |
| |
| # now make a final pass to finalize op_desc fields that may depend |
| # on the register enumeration |
| for op_desc in self.items: |
| op_desc.finalize(self.predRead, self.predWrite) |
| |
| def __len__(self): |
| return len(self.items) |
| |
| def __getitem__(self, index): |
| return self.items[index] |
| |
| def append(self, op_desc): |
| self.items.append(op_desc) |
| self.bases[op_desc.base_name] = op_desc |
| |
| def find_base(self, base_name): |
| # like self.bases[base_name], but returns None if not found |
| # (rather than raising exception) |
| return self.bases.get(base_name) |
| |
| # internal helper function for concat[Some]Attr{Strings|Lists} |
| def __internalConcatAttrs(self, attr_name, filter, result): |
| for op_desc in self.items: |
| if filter(op_desc): |
| result += getattr(op_desc, attr_name) |
| return result |
| |
| # return a single string that is the concatenation of the (string) |
| # values of the specified attribute for all operands |
| def concatAttrStrings(self, attr_name): |
| return self.__internalConcatAttrs(attr_name, lambda x: 1, '') |
| |
| # like concatAttrStrings, but only include the values for the operands |
| # for which the provided filter function returns true |
| def concatSomeAttrStrings(self, filter, attr_name): |
| return self.__internalConcatAttrs(attr_name, filter, '') |
| |
| # return a single list that is the concatenation of the (list) |
| # values of the specified attribute for all operands |
| def concatAttrLists(self, attr_name): |
| return self.__internalConcatAttrs(attr_name, lambda x: 1, []) |
| |
| # like concatAttrLists, but only include the values for the operands |
| # for which the provided filter function returns true |
| def concatSomeAttrLists(self, filter, attr_name): |
| return self.__internalConcatAttrs(attr_name, filter, []) |
| |
| def sort(self): |
| self.items.sort(key=lambda a: a.sort_pri) |
| |
| class SubOperandList(OperandList): |
| '''Find all the operands in the given code block. Returns an operand |
| descriptor list (instance of class OperandList).''' |
| def __init__(self, parser, code, requestor_list): |
| self.items = [] |
| self.bases = {} |
| # delete strings and comments so we don't match on operands inside |
| for regEx in (stringRE, commentRE): |
| code = regEx.sub('', code) |
| |
| # search for operands |
| for match in parser.operandsRE().finditer(code): |
| op = match.groups() |
| # regexp groups are operand full name, base, and extension |
| (op_full, op_base, op_ext) = op |
| # If is a elem operand, define or update the corresponding |
| # vector operand |
| if op_base in parser.elemToVector: |
| elem_op = op_base |
| op_base = parser.elemToVector[elem_op] |
| # find this op in the requestor list |
| op_desc = requestor_list.find_base(op_base) |
| if not op_desc: |
| error('Found operand %s which is not in the requestor list!' |
| % op_base) |
| else: |
| # See if we've already found this operand |
| op_desc = self.find_base(op_base) |
| if not op_desc: |
| # if not, add a reference to it to this sub list |
| self.append(requestor_list.bases[op_base]) |
| |
| self.sort() |
| |
| pcs = list(filter(lambda i: i.isPCState(), self.items)) |
| mem = list(filter(lambda i: i.isMem(), self.items)) |
| |
| if len(mem) > 1: |
| error("Code block has more than one memory operand") |
| |
| part = any(p.isPCPart() for p in pcs) |
| whole = any(not p.isPCPart() for p in pcs) |
| |
| if part and whole: |
| error("Mixed whole and partial PC state operands") |
| |
| self.memOperand = mem[0] if mem else None |
| |
| # Whether the whole PC needs to be read so parts of it can be accessed |
| self.readPC = any(i.isPCPart() for i in self.items) |
| # Whether the whole PC needs to be written after parts of it were |
| # changed |
| self.setPC = any(i.isPCPart() and i.is_dest for i in self.items) |
| # Whether this instruction manipulates the whole PC or parts of it. |
| # Mixing the two is a bad idea and flagged as an error. |
| self.pcPart = None |
| if part: self.pcPart = True |
| if whole: self.pcPart = False |
| |
| # Flags to keep track if one or more operands are to be read/written |
| # conditionally. |
| self.predRead = any(i.hasReadPred() for i in self.items) |
| self.predWrite = any(i.hasWritePred() for i in self.items) |