benchmarks

This commit is contained in:
Andreas Abel
2020-03-19 21:54:32 +01:00
parent 33f91e811a
commit b7315a4dfb
7 changed files with 3873 additions and 0 deletions

174
tools/cpuBench/addAMDDocToXML.py Executable file
View File

@@ -0,0 +1,174 @@
#!/usr/bin/python
from collections import namedtuple
import xml.etree.ElementTree as ET
from xml.dom import minidom
import argparse
import re
from openpyxl import load_workbook
DocEntry = namedtuple('DocEntry', ['mnemonic', 'operands', 'ops', 'unit', 'lat', 'tp'])
def main():
parser = argparse.ArgumentParser(description="Add data to XML file from AMD's doc")
parser.add_argument('-xml')
parser.add_argument('-xlsx')
parser.add_argument('-outputXML')
parser.add_argument('-arch')
args = parser.parse_args()
docEntrySet = set()
mnemonicMap = dict()
wb = load_workbook(args.xlsx, data_only=True)
ws = wb.active
for row in ws.iter_rows(min_row=2, values_only=True):
mnemonicStr = row[0]
if not mnemonicStr: continue
if mnemonicStr.endswith('cc'):
mnemonics = [mnemonicStr.replace('cc', p) for p in ['B', 'BE', 'L', 'LE', 'NB', 'NBE', 'NL', 'NLE', 'NO', 'NP', 'NS', 'NZ', 'O', 'P', 'S', 'Z']]
else:
mnemonics = mnemonicStr.replace(' (near)', '').replace('cc', '').split('/')
for mnemonic in mnemonics:
if mnemonic in ['AAA', 'AAD', 'AAM', 'AAS', 'ARPL', 'BOUND', 'DAA', 'DAS', 'INTO', 'JCXZ', 'LDS', 'LES','POPA', 'POPAD', 'POPD', 'POPFD', 'PUSHA', 'PUSHAD', 'PUSHFD']:
# 32-bit instructions
continue
if mnemonic in ['CMPS', 'FCLEX', 'FINIT', 'FSAVE', 'FSTCW', 'FSTENV', 'FSTSW', 'INS', 'LODS', 'LOOPNZ', 'LOOPZ', 'MOVS', 'OUTS', 'PCLMULHQHQDQ', 'PCLMULHQLQDQ', 'PCLMULLQHQDQ', 'PCLMULLQLQDQ', 'RDPRU', 'SAL', 'SCAS', 'STOS', 'VGATHERDD', 'VGATHERDQ', 'VGATHERQD', 'VGATHERQQ','VPCLMULHQHQDQ', 'VPCLMULHQLQDQ', 'VPCLMULLQHQDQ', 'VPCLMULLQLQDQ', 'WAIT', 'XLATB']:
# missing in XED
continue
if mnemonic in ['INT1', 'JECXZ']:
# missing from XML file
continue
operands = row[1:5]
ops = row[7]
if ops == 'not supported':
continue
unit = row[8]
lat = row[9]
tp = row[10]
de = DocEntry(mnemonic, operands, ops, unit, lat, tp)
docEntrySet.add(de)
mnemonicMap.setdefault(mnemonic, []).append(de)
iclassAsmDict = dict()
root = ET.parse(args.xml).getroot()
for instrNode in root.iter('instruction'):
if instrNode.attrib.get('evex', '') == '1':
continue
if instrNode.attrib['extension'] == 'VAES':
continue
iclass = instrNode.attrib['iclass']
asm = instrNode.attrib['asm']
iclassAsmDict.setdefault(iclass, set()).add(instrNode)
iclassAsmDict.setdefault(re.sub('{.*} ', '', asm), set()).add(instrNode)
#for x in set(op for de in docList for op in de.operands):
# print x
xmlToDocDict = dict()
for de in sorted(docEntrySet):
if de.mnemonic not in iclassAsmDict:
print 'no XML entry found for ' + str(de)
xmlFound = False
for instrNode in iclassAsmDict[de.mnemonic]:
explXmlOperands = [op for op in instrNode.findall('./operand') if not op.attrib.get('suppressed', '') == '1' and not op.attrib.get('implicit', '') == '1']
docOperands = [op for op in de.operands if op is not None]
if (not docOperands and any(op.attrib['type'] == 'mem' for op in explXmlOperands) and
any(len(instrNode2.findall('./operand[@type="mem"]')) == 0 for instrNode2 in iclassAsmDict[de.mnemonic] if instrNode != instrNode2)):
continue
if docOperands and explXmlOperands and (len(explXmlOperands) != len(docOperands)):
if any(len(explXmlOperands) == len([op for op in de2.operands if op is not None]) for de2 in mnemonicMap[de.mnemonic] if de!=de2):
continue
if docOperands and explXmlOperands:
xmlOperands = explXmlOperands
else:
xmlOperands = [op for op in instrNode.findall('./operand')]
invalid = False
for docOp, xmlOp in zip(docOperands, xmlOperands):
if de.mnemonic in ['CLZERO']: continue
if xmlOp.attrib['type'] == 'mem' and set(de.operands) == {None}:
invalid = True
break
if docOp is None: continue
if docOp in ['pntr16/mem16:16/32']: continue
if xmlOp.attrib['type'] == 'reg':
if docOp == 'segmentReg':
if xmlOp.attrib.get('implicit', '') == '1': continue
elif docOp in ['reg', 'reg/mem'] and xmlOp.attrib.get('implicit', '') != '1': continue
elif not 'MM' in xmlOp.text:
if docOp == 'Sti' and xmlOp.text.startswith('ST'): continue
if docOp == 'ax' and xmlOp.text == 'AX': continue
if 'width' in xmlOp.attrib and re.search('reg(\d+/)*' + xmlOp.attrib['width'], docOp) is not None: continue
else:
if 'mmx' in docOp and xmlOp.text.startswith('MM'): continue
if 'xmm' in docOp and xmlOp.text.startswith('XMM'): continue
if 'ymm' in docOp and xmlOp.text.startswith('YMM'): continue
elif xmlOp.attrib['type'] == 'mem':
if docOp in ['mem', 'reg/mem', 'xmm2/mem', 'vm32x']: continue
if re.search('mem(\d+/)*' + xmlOp.attrib['width'], docOp) is not None: continue
elif xmlOp.attrib['type'] in ['imm', 'relbr']:
if docOp in ['imm', 'imm`', 'CL/Imm', 'xmm3/imm']: continue
if re.search('imm(\d+/)*' + xmlOp.attrib['width'], docOp) is not None: continue
invalid = True
if invalid:
continue
if instrNode in xmlToDocDict:
if (set(de.operands) != {None}) and (set(xmlToDocDict[instrNode].operands) == {None}):
xmlFound = True
xmlToDocDict[instrNode] = de
elif (set(de.operands) == {None}) and (set(xmlToDocDict[instrNode].operands) != {None}):
pass
else:
print 'duplicate entry for ' + instrNode.attrib['string'] + ' found: ' + str(list(xmlToDocDict[instrNode])) + ', ' + str(list(de))
else:
xmlFound = True
xmlToDocDict[instrNode] = de
if not xmlFound:
print 'no matching XML entry found for ' + str(de)
print 'Found data for ' + str(len(xmlToDocDict)) + ' instruction variants'
for instrNode, de in xmlToDocDict.items():
archNode = instrNode.find('./architecture[@name="{}"]'.format(args.arch))
if archNode is None:
archNode = ET.SubElement(instrNode, "architecture")
archNode.attrib['name'] = args.arch
docNode = ET.SubElement(archNode, "doc")
if de.ops: docNode.attrib['uops'] = str(de.ops)
if de.unit: docNode.attrib['ports'] = str(de.unit)
if de.lat and de.lat != '-': docNode.attrib['latency'] = str(de.lat)
if de.tp:
try:
if str(de.tp) == '0.33':
docNode.attrib['TP'] = '3.00'
else:
docNode.attrib['TP'] = format(1/float(de.tp), '.2f')
except ValueError:
docNode.attrib['TP'] = de.tp
with open(args.outputXML, "w") as f:
rough_string = ET.tostring(root, 'utf-8')
reparsed = minidom.parseString(rough_string)
f.write('\n'.join([line for line in reparsed.toprettyxml(indent=' '*2).split('\n') if line.strip()]))
if __name__ == "__main__":
main()

68
tools/cpuBench/addDocToXML.py Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/python
from collections import namedtuple
import xml.etree.ElementTree as ET
from xml.dom import minidom
import argparse
DocEntry = namedtuple('DocEntry', ['iform', 'regsize', 'mask', 'tp', 'lat'])
def main():
parser = argparse.ArgumentParser(description="Add data to XML file from Intel's CSV doc")
parser.add_argument('-xml')
parser.add_argument('-csv')
parser.add_argument('-outputXML')
parser.add_argument('-arch')
args = parser.parse_args()
docDict = dict()
with open(args.csv, 'r') as f:
for i, line in enumerate(f):
if i > 0:
de = DocEntry(*line.strip().split(','))
docDict.setdefault(de.iform, []).append(de)
root = ET.parse(args.xml).getroot()
for instrNode in root.iter('instruction'):
iform = instrNode.attrib['iform']
if iform in docDict:
matchingDEs = set(docDict[iform])
if len(matchingDEs) > 1:
for de in list(matchingDEs):
if de.regsize != '-':
if not instrNode.findall('./operand[@type="reg"][@width="{}"]'.format(de.regsize)):
matchingDEs.remove(de)
for de in list(matchingDEs):
if 'mask' in instrNode.attrib:
if (instrNode.attrib['mask'] == '1' and de.mask == 'no') or (instrNode.attrib['mask'] == '0' and de.mask == 'yes'):
matchingDEs.remove(de)
if len(matchingDEs) == 0:
print 'No matching iform: ' + iform
elif len(matchingDEs) > 1:
print 'Multiple matching iforms: ' + iform
else:
de = next(iter(matchingDEs))
archNode = instrNode.find('./architecture[@name="{}"]'.format(args.arch))
if archNode is None:
archNode = ET.SubElement(instrNode, "architecture")
archNode.attrib['name'] = args.arch
docNode = ET.SubElement(archNode, "doc")
if de.tp: docNode.attrib['TP'] = de.tp
if de.lat: docNode.attrib['latency'] = de.lat
with open(args.outputXML, "w") as f:
rough_string = ET.tostring(root, 'utf-8')
reparsed = minidom.parseString(rough_string)
f.write('\n'.join([line for line in reparsed.toprettyxml(indent=' '*2).split('\n') if line.strip()]))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,211 @@
#!/usr/bin/python
import xml.etree.ElementTree as ET
import argparse
import sys
from utils import *
def main():
parser = argparse.ArgumentParser(description='Compare results')
parser.add_argument("-input", help="Input XML file", default='result.xml')
parser.add_argument("-arch", help="Consider only this architecture")
parser.add_argument("-ignoreLockRep", help="Ignore Instructions with lock and rep prefixes", action='store_true')
parser.add_argument("-verbose", help="Verbose mode", action='store_true')
args = parser.parse_args()
root = ET.parse(args.input)
instrArchNodes = []
for instrNode in root.iter('instruction'):
if args.ignoreLockRep and ('LOCK_' in instrNode.attrib['iform'] or 'REP_' in instrNode.attrib['iform']): continue
archNode = instrNode.find('./architecture[@name="{}"]'.format(args.arch))
if archNode is not None:
instrArchNodes.append((instrNode, archNode))
nPortsMeasurementOnly = 0
nPortsOtherOnly = 0
nPortsBoth = 0
nPortsEq = 0
nPortsDiff = 0
nUopsMeasurementOnly = 0
nUopsOtherOnly = 0
nUopsBoth = 0
nUopsEq = 0
nUopsEqPortsEq = 0
nUopsEqPortsDiff = 0
nUopsDiff = 0
nLatMeasurementOnly = 0
nLatOtherOnly = 0
nLatBoth = 0
nLatUB = 0
nLatUBCorrect = 0
nLatUBExact = 0
nLatUBClose = 0
nLatUBIncorrect = 0
nLatNoUB = 0
nLatNoUBMaxEq = 0
nLatNoUBMaxDiff = 0
for instrNode, archNode in instrArchNodes:
measurementNode = archNode.find('measurement')
nonMeasurementNodes = archNode.findall('./IACA') + archNode.findall('doc')
otherPorts = [v for m in nonMeasurementNodes for a,v in m.attrib.items() if a.startswith('ports')]
mPorts = ([v for a, v in measurementNode.attrib.items() if a.startswith('ports')] if measurementNode is not None else [])
portsEq = False
portsDiff = False
if mPorts:
if otherPorts:
nPortsBoth += 1
if any(m in otherPorts for m in mPorts):
portsEq = True
nPortsEq += 1
else:
portsDiff = True
nPortsDiff += 1
if args.verbose: print 'PortsDiff: {} - {} - {}'.format(instrNode.attrib['string'], mPorts, otherPorts)
else:
nPortsMeasurementOnly += 1
else:
if otherPorts:
nPortsOtherOnly += 1
if args.verbose: print 'PortsOtherOnly: ' + instrNode.attrib['string']
otherUops = [v for m in nonMeasurementNodes for a,v in m.attrib.items() if a.startswith('uops') and v.replace('.','',1).isdigit()]
mUops = ([v for a,v in measurementNode.attrib.items() if a.startswith('uops') and not 'retire_slots' in a] if measurementNode is not None else [])
if mUops:
if otherUops:
nUopsBoth += 1
if any(m in otherUops for m in mUops):
nUopsEq += 1
nUopsEqPortsEq += int(portsEq)
nUopsEqPortsDiff += int(portsDiff)
else:
nUopsDiff += 1
if args.verbose: print 'UopsDiff: {} - {} - {}'.format(instrNode.attrib['string'], mUops, otherUops)
else:
nUopsMeasurementOnly += 1
else:
if otherUops:
nUopsOtherOnly += 1
if args.verbose: print 'UopsOtherOnly: ' + instrNode.attrib['string']
otherLatencies = [float(v) for m in nonMeasurementNodes for a,v in m.attrib.items() if a.startswith('latency') and v.replace('.','',1).isdigit()]
latEntry = getLatencyTableEntry(measurementNode)
if latEntry is not None:
if otherLatencies:
nLatBoth += 1
_, _, _, maxLat, maxLatUB = latEntry
if maxLatUB:
nLatUB += 1
if any(x for x in otherLatencies if x <= maxLat):
nLatUBCorrect += 1
if maxLat in otherLatencies:
nLatUBExact += 1
diff = min(abs(float(maxLat)-float(o)) for o in otherLatencies)
if diff <= 1.01:
nLatUBClose += 1
else:
nLatUBIncorrect += 1
if args.verbose: print 'LatUBIncorrect: {} - {} - {}'.format(instrNode.attrib['string'], maxLat, otherLatencies)
else:
nLatNoUB += 1
if maxLat in otherLatencies:
nLatNoUBMaxEq += 1
else:
nLatNoUBMaxDiff += 1
if args.verbose: print 'LatNoUBMaxDiff: {} - {} - {}'.format(instrNode.attrib['string'], maxLat, otherLatencies)
else:
nLatMeasurementOnly += 1
else:
if otherLatencies:
nLatOtherOnly += 1
if args.verbose: print 'LatOtherOnly: ' + instrNode.attrib['string']
print 'Ports:'
print ' Measurement data only: ' + str(nPortsMeasurementOnly)
print ' Other data only: ' + str(nPortsOtherOnly)
print ' Both: ' + str(nPortsBoth)
print ' Eq: ' + str(nPortsEq)
print ' Diff: ' + str(nPortsDiff)
print ''
print 'Uops:'
print ' Measurement data only: ' + str(nUopsMeasurementOnly)
print ' Other data only: ' + str(nUopsOtherOnly)
print ' Both: ' + str(nUopsBoth)
print ' Eq: ' + str(nUopsEq)
print ' PortsEq: ' + str(nUopsEqPortsEq)
print ' PortsDiff: ' + str(nUopsEqPortsDiff)
print ' Diff: ' + str(nUopsDiff)
print ''
print 'Latency:'
print ' Measurement data only: ' + str(nLatMeasurementOnly)
print ' Other data only: ' + str(nLatOtherOnly)
print ' Both: ' + str(nLatBoth)
print ' Exact: ' + str(nLatNoUB)
print ' Eq (Max): ' + str(nLatNoUBMaxEq)
print ' Diff (Max): ' + str(nLatNoUBMaxDiff)
print ' Upper Bound: ' + str(nLatUB)
print ' Correct: ' + str(nLatUBCorrect)
print ' Exact: ' + str(nLatUBExact)
print ' Close: ' + str(nLatUBClose)
print ' Incorrect: ' + str(nLatUBIncorrect)
print ''
print 'Throughput:'
for TP_m, TP_o in [('TP', 'TP'), ('TP_ports', 'TP'), ('TP', 'TP_ports'), ('TP_ports', 'TP_ports')]:
nTPMeasurementOnly = 0
nTPOtherOnly = 0
nTPBoth = 0
nTPEq = 0
nTPDiff = 0
nTPClose = 0
nTPNotClose = 0
for instrNode, archNode in instrArchNodes:
measurementNode = archNode.find('measurement')
nonMeasurementNodes = archNode.findall('./IACA') + archNode.findall('doc')
otherTPs = [float(v) for m in nonMeasurementNodes for a,v in m.attrib.items() if a in [TP_o, TP_o+'_same_reg'] and v.replace('.','',1).isdigit()]
mTPs = ([float(v) for a, v in measurementNode.attrib.items() if a in [TP_m, TP_m+'_same_reg']] if measurementNode is not None else [])
if mTPs:
if otherTPs:
nTPBoth += 1
if any(m in otherTPs for m in mTPs):
nTPEq += 1
else:
nTPDiff += 1
if args.verbose: print 'TPDiff ({} (measurements) - {} (other)): {} - {} - {}'.format(TP_m, TP_o, instrNode.attrib['string'], mTPs, otherTPs)
diff = min(abs(float(m)-float(o)) for o in otherTPs for m in mTPs)
if diff <= .1:
nTPClose += 1
else:
nTPNotClose += 1
if args.verbose: print 'TPNotClose ({} (measurements) - {} (other)): {} - {} - {}'.format(TP_m, TP_o, instrNode.attrib['string'], mTPs, otherTPs)
else:
nTPMeasurementOnly += 1
else:
if otherTPs:
nTPOtherOnly += 1
if args.verbose: print 'TPOtherOnly ({} (measurements) - {} (other)): {}'.format(TP_m, TP_o, instrNode.attrib['string'])
print ' {} (measurements) - {} (other):'.format(TP_m, TP_o)
print ' Measurement data only: ' + str(nTPMeasurementOnly)
print ' Other data only: ' + str(nTPOtherOnly)
print ' Both: ' + str(nTPBoth)
print ' Eq: ' + str(nTPEq)
print ' Diff: ' + str(nTPDiff)
print ' Close: ' + str(nTPClose)
print ' NotClose: ' + str(nTPNotClose)
if __name__ == "__main__":
main()

81
tools/cpuBench/compareXML.py Executable file
View File

@@ -0,0 +1,81 @@
#!/usr/bin/python
import xml.etree.ElementTree as ET
from xml.dom import minidom
import argparse
# Shows the differences between two XML files for a specific microarchitecture
def main():
parser = argparse.ArgumentParser(description='Compare XML files')
parser.add_argument('inp1')
parser.add_argument('arch1')
parser.add_argument('inp2')
parser.add_argument('arch2')
parser.add_argument('-TP', action='store_true')
parser.add_argument('-lat', action='store_true')
parser.add_argument('-ports', action='store_true')
args = parser.parse_args()
root1 = ET.parse(args.inp1).getroot()
root2 = ET.parse(args.inp2).getroot()
instrNodeDict1 = {instrNode.attrib['string']: instrNode for instrNode in root1.iter('instruction')}
instrNodeDict2 = {instrNode.attrib['string']: instrNode for instrNode in root2.iter('instruction')}
tpDiff = 0
latDiff = 0
portsDiff = 0
for instrStr in sorted(instrNodeDict1):
instrNode1 = instrNodeDict1[instrStr]
if not instrStr in instrNodeDict2:
print 'No matching entry found for ' + instrStr
continue
instrNode2 = instrNodeDict2[instrStr]
for mNode1 in instrNode1.findall('./architecture[@name="' + args.arch1 + '"]/measurement'):
for mNode2 in instrNode2.findall('./architecture[@name="' + args.arch2 + '"]/measurement'):
if args.TP:
tp1 = mNode1.attrib['TP']
tp2 = mNode2.attrib['TP']
if tp1 != tp2:
tpDiff += 1
print instrStr + ' - TP1: ' + tp1 + ' - TP2: ' + tp2
if args.lat:
for latNode1, latNode2 in zip(mNode1.findall('./latency'), mNode2.findall('./latency')):
latStr1 = ET.tostring(latNode1, encoding='utf-8').strip()
latStr2 = ET.tostring(latNode2, encoding='utf-8').strip()
if latStr1 != latStr2:
latDiff += 1
print instrStr
print ' ' + latStr1
print ' ' + latStr2
if args.ports:
p1 = mNode1.attrib.get('ports', '')
p2 = mNode2.attrib.get('ports', '')
if p1 != p2:
portsDiff += 1
print instrStr + ' - P1: ' + p1 + ' - P2: ' + p2
if not args.TP and not args.lat and not args.ports:
xmlStr1 = ET.tostring(mNode1, encoding='utf-8').strip()
xmlStr2 = ET.tostring(mNode2, encoding='utf-8').strip()
if xmlStr1 != xmlStr2:
print '-------------------------------'
print instrStr
print xmlStr1
print xmlStr2
print '-------------------------------'
if args.TP:
print 'TPDiff: ' + str(tpDiff)
if args.lat:
print 'LatDiff: ' + str(latDiff)
if args.ports:
print 'portsDiff: ' + str(portsDiff)
if __name__ == "__main__":
main()

3099
tools/cpuBench/cpuBench.py Executable file

File diff suppressed because it is too large Load Diff

42
tools/cpuBench/mergeXML.py Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/python
import xml.etree.ElementTree as ET
from xml.dom import minidom
import argparse
import datetime
# If inp2 contains a measurement node for an architecture for which inp1 does not contain a measurement node, the node is added to a copy of inp1.
def main():
parser = argparse.ArgumentParser(description='Merge XML files')
parser.add_argument('inp1')
parser.add_argument('inp2')
parser.add_argument('outp')
args = parser.parse_args()
root1 = ET.parse(args.inp1).getroot()
root2 = ET.parse(args.inp2).getroot()
instrNode2Dict = {instrNode.attrib['string']: instrNode for instrNode in root2.iter('instruction')}
root1.attrib['date'] = str(datetime.date.today())
for instrNode1 in root1.iter('instruction'):
if instrNode1.attrib['string'] not in instrNode2Dict:
print 'no matching entry found for ' + instrNode1.attrib['string']
continue
for instrNode2 in instrNode2Dict[instrNode1.attrib['string']]:
for archNode2 in instrNode2.iter('architecture'):
archNode1 = instrNode1.find('./architecture[@name="' + archNode2.attrib['name'] + '"]')
if archNode1 is not None:
if archNode1.findall('./measurement'): continue
for measurementNode in archNode2.findall('./measurement'):
archNode1.append(measurementNode)
else:
instrNode1.append(archNode2)
with open(args.outp, "w") as f:
rough_string = ET.tostring(root1, 'utf-8')
reparsed = minidom.parseString(rough_string)
f.write('\n'.join([line for line in reparsed.toprettyxml(indent=' '*2).split('\n') if line.strip()]))
if __name__ == "__main__":
main()

198
tools/cpuBench/utils.py Executable file
View File

@@ -0,0 +1,198 @@
import re
import sys
from scipy.optimize import linprog
def addHTMLCodeForOperands(instrNode, html):
if instrNode.find('operand') is not None:
html.append('<h2>Operands</h2>')
html.append('<ul>')
for opNode in instrNode.iter('operand'):
line = 'Operand ' + opNode.attrib['idx']
properties = []
for prop in ['r', 'w']:
if opNode.attrib.get(prop, '0') == '1': properties.append(prop)
if properties: properties = ['/'.join(properties)]
if opNode.attrib.get('undef', '0') == '1': properties.append('undefined')
if opNode.attrib.get('suppressed', '0') == '1': properties.append('suppressed')
if opNode.attrib.get('optional', '0') == '1': properties.append('optional')
line += ' (' + ', '.join(properties) + '): '
if opNode.attrib['type'] == 'reg':
line += 'Register (' + opNode.text.replace(',', ', ') + ')'
elif opNode.attrib['type'] == 'mem':
line += 'Memory'
if 'asm-prefix' in opNode.attrib: line.append(' (' + opNode.attrib['asm-prefix'] + ')')
elif opNode.attrib['type'] == 'flags':
line += 'Flags ('
first = True
for k, v in opNode.attrib.items():
if k.startswith('flag_'):
if not first: line += ', '
line += k[5:] + ': ' + v
first = False
line += ')'
elif opNode.attrib['type'] == 'imm':
line += opNode.attrib['width'] + '-bit immediate'
if opNode.attrib.get('implicit', '') == '1':
line += ' (implicit): ' + opNode.text
html.append('<li>' + line + '</li>')
html.append('</ul>')
def canonicalizeInstrString(instrString):
return re.sub('[(){}, ]+', '_', instrString).strip('_')
def getTP_LP(PU):
if len(PU) == 0:
return 0
if len(PU) == 1:
pc, uops = PU[0]
return round(float(uops)/len(pc), 2)
ports = list(set.union(*[set(pc) for pc, _ in PU]))
zeroConstraint = []
for p in ports:
for pc, uops in PU:
if not p in pc:
zeroConstraint.append(1)
else:
zeroConstraint.append(0)
zeroConstraint.append(0) #z
nonZeroConstraints = []
nonZeroConstraintsRHS = []
for pu in PU:
pc, uops = pu
nonZeroConstraintsRHS.append(uops)
nonZeroConstraint = []
for p in ports:
for pu2 in PU:
if pu != pu2 or p not in pc:
nonZeroConstraint.append(0)
else:
nonZeroConstraint.append(1)
nonZeroConstraint.append(0) #z
nonZeroConstraints.append(nonZeroConstraint)
A_eq = [zeroConstraint] + nonZeroConstraints
b_eq = [0] + nonZeroConstraintsRHS
zConstraints = []
for p in ports:
zConstraint = []
for p2 in ports:
for pu in PU:
if p != p2:
zConstraint.append(0)
else:
zConstraint.append(1)
zConstraint.append(-1)
zConstraints.append(zConstraint)
A_ub = zConstraints
b_ub = [0] * len(zConstraints)
c = [0]*(len(PU)*len(ports)) + [1]
res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq)
return round(res.fun, 2)
# Example output: "Latency operand 2 -> 1 (memory): <=3"
def latencyNodeToStr(latNode, sameReg, addr_mem):
suffix = ('_'+addr_mem if addr_mem else '') + ('_same_reg' if sameReg else '')
if not any((a in ['cycles'+suffix, 'min_cycles'+suffix]) for a in latNode.attrib):
return None
ret = 'Latency operand ' + latNode.attrib['start_op'] + ' &rarr; ' + latNode.attrib['target_op']
if sameReg:
ret += ', with the same register for different operands'
if addr_mem == 'addr':
ret += ' (address, base register)'
elif addr_mem == 'addr_VSIB':
ret += ' (address, index register)'
elif addr_mem == 'mem':
ret += ' (memory)'
ret += ': '
if 'cycles'+suffix in latNode.attrib:
if latNode.attrib.get('cycles'+suffix+'_is_upper_bound', '') == '1':
ret += '&le;'
cycles = latNode.attrib['cycles'+suffix]
ret += cycles
else:
minCycles = latNode.attrib['min_cycles'+suffix]
maxCycles = latNode.attrib['max_cycles'+suffix]
if latNode.attrib.get('min_cycles'+suffix+'_is_upper_bound', '') == '1':
ret += '&le;' + minCycles
else:
ret += minCycles + ' &le; lat &le; ' + maxCycles
return ret
# Returns (string, minLat, minLatUB, maxLat, maxLatUB)
# Example output: ("[1;<=7]", 1, False, 7, True)
def getLatencyTableEntry(measurementNode):
if measurementNode is None or measurementNode.find('./latency') is None:
return None
minLat = sys.maxint
maxLat = 0
minLatUB = False
maxLatUB = False
for latNode in measurementNode.findall('./latency'):
for sameReg in [False, True]:
for addr_mem in ['', 'addr', 'mem']:
suffix = ('_'+addr_mem if addr_mem else '') + ('_same_reg' if sameReg else '')
if 'cycles'+suffix in latNode.attrib:
cycles = int(latNode.attrib['cycles'+suffix])
isUB = (latNode.attrib.get('cycles'+suffix+'_is_upper_bound', '') == '1')
if cycles == maxLat:
maxLatUB = (maxLatUB and isUB)
elif cycles > maxLat:
maxLat = cycles
maxLatUB = isUB
if cycles == minLat:
minLatUB = (minLatUB or isUB)
elif cycles < minLat:
minLat = cycles
minLatUB = isUB
if 'max_cycles'+suffix in latNode.attrib:
cycles = int(latNode.attrib['max_cycles'+suffix])
isUB = (latNode.attrib.get('max_cycles'+suffix+'_is_upper_bound', '') == '1')
if cycles == maxLat:
maxLatUB = (maxLatUB and isUB)
elif cycles > maxLat:
maxLat = cycles
maxLatUB = isUB
if 'min_cycles'+suffix in latNode.attrib:
cycles = float(latNode.attrib['min_cycles'+suffix])
isUB = (latNode.attrib.get('min_cycles'+suffix+'_is_upper_bound', '') == '1')
if cycles == minLat:
minLatUB = (minLatUB or isUB)
elif cycles < minLat:
minLat = cycles
minLatUB = isUB
if minLat == maxLat:
latStr = str(maxLat)
if minLatUB or maxLatUB:
latStr = '&le;' + latStr
else:
latStr = '['
if minLatUB:
latStr += '&le;'
latStr += str(minLat)
latStr += ';'
if maxLatUB:
latStr += '&le;'
latStr += str(maxLat)
latStr += ']'
return (latStr, minLat, minLatUB, maxLat, maxLatUB)