// -*- mode:c++ -*- // Copyright (c) 2018 Metempsy Technology Consulting // 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. // // 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. // // Authors: Jordi Vaquero let {{ import math OP_DICT = { "CAS" : 'if (a == *b){*b = c;}', "SWP" : '*b = c;', "ADD" : '*b += c;', "EOR" : '*b ^= c;', "CLR" : '*b &= ~c;', "SET" : '*b |= c;', "MAX" : '*b = std::max(*b, c);', "MIN" : '*b = std::min(*b, c);', } MASKS = { 1: 0xFF, 2: 0xFFFF, 4: 0xFFFFFFFF, 8: 0xFFFFFFFFFFFFFFFF, } header_output = "" decoder_output = "" exec_output = "" class AtomicInst64(LoadStoreInst): execBase = 'AtomicInst64' micro = False def __init__(self, mnem, Name, size=4, user=False, flavor="normal", unsign=True, top = False, paired=False): super(AtomicInst64, self).__init__() self.name= mnem self.Name = Name self.size = size self.user = user self.flavor = flavor self.unsign = unsign self.top = top self.paired = paired self.memFlags = ["ArmISA::TLB::MustBeOne"] self.instFlags = ["IsAtomic"] self.codeBlobs = { "postacc_code" : "" } self.codeBlobs['usrDecl'] = "" # Add memory request flags where necessary if self.user: self.memFlags.append("ArmISA::TLB::UserMode") sz = self.size*2 if paired else self.size self.memFlags.append("%d" % int(math.log(sz, 2))) if self.micro: self.instFlags.append("IsMicroop") if self.flavor in ("release", "acquire_release", "acquire"): self.instFlags.append("IsMemBarrier") if self.flavor in ("release", "acquire_release"): self.instFlags.append("IsWriteBarrier") if self.flavor in ("acquire_release", "acquire"): self.instFlags.append("IsReadBarrier") self.memFlags.append('Request::ATOMIC_RETURN_OP') def emitHelper(self, base = 'Memory64', wbDecl = None, ): global header_output, decoder_output, exec_output # If this is a microop itself, don't allow anything that would # require further microcoding. if self.micro: assert not wbDecl fa_code = None if not self.micro : #and self.flavor in ("normal", "release"): fa_code = ''' fault->annotate(ArmFault::SAS, %s); fault->annotate(ArmFault::SSE, false); fault->annotate(ArmFault::SRT, dest); fault->annotate(ArmFault::SF, %s); fault->annotate(ArmFault::AR, %s); ''' % ("0" if self.size == 1 else "1" if self.size == 2 else "2" if self.size == 4 else "3", "true" if self.size == 8 else "false", "true" if self.flavor != "normal" else "false") sas_code = "3" if self.size == 1 : sas_code = "0" elif self.size == 2: sas_code = "1" elif self.size == 4: sas_code = "2" if self.paired and sas_code == "3": sas_code = "4" if self.paired and sas_code == "2": sas_code = "3" fa_code = ''' fault->annotate(ArmFault::SAS, %s); fault->annotate(ArmFault::SSE, %s); fault->annotate(ArmFault::SRT, dest); fault->annotate(ArmFault::SF, %s); fault->annotate(ArmFault::AR, %s); ''' % (sas_code, "true" if not self.unsign else "false", "true" if self.size == 8 else "false", "true" if self.flavor != "normal" else "false") (newHeader, newDecoder, newExec) = \ self.fillTemplates(self.name, self.Name, self.codeBlobs, self.memFlags, self.instFlags, base, wbDecl, faCode=fa_code) header_output += newHeader decoder_output += newDecoder exec_output += newExec def buildEACode(self): # Address computation eaCode = SPAlignmentCheckCode + "EA = XBase" if self.size == 16: if self.top: eaCode += " + (isBigEndian64(xc->tcBase()) ? 0 : 8)" else: eaCode += " + (isBigEndian64(xc->tcBase()) ? 8 : 0)" if not self.post: eaCode += self.offset eaCode += ";" self.codeBlobs["ea_code"] = eaCode class AtomicSingleOp(AtomicInst64): decConstBase = 'AmoOp' base = 'ArmISA::MemoryEx64' writeback = True post = False execBase = 'AmoOp' def __init__(self, *args, **kargs): super(AtomicSingleOp, self).__init__(*args, **kargs) self.suffix = buildMemSuffix(not self.unsign, self.size) if self.size == 8: self.res = 'XResult_ud' #if self.unsign else 'XResult_sd' self.des = 'XDest_ud' #if self.unsign else 'XDest_sd' self.tp = 'uint64_t' if self.unsign else 'int64_t' self.utp = 'uint64_t' self.suffix = '_sd' if not self.unsign else '_ud' elif self.size == 4: self.res = 'XResult_uw' #if self.unsign else 'XResult_sw' self.des = 'XDest_uw' #if self.unsign else 'XDest_sw' self.tp = 'uint32_t' if self.unsign else 'int32_t' self.utp = 'uint32_t' elif self.size == 2: self.res = 'XResult_uh' #if self.unsign else 'XResult_sh' self.des = 'XDest_uh' #if self.unsign else 'XDest_sh' self.tp = 'uint16_t' if self.unsign else 'int16_t' self.utp = 'uint16_t' elif self.size == 1: self.res = 'XResult_ub' #if self.unsign else 'XResult_sb' self.des = 'XDest_ub' #if self.unsign else 'XDest_sb' self.tp = 'uint8_t' if self.unsign else 'int8_t' self.utp = 'uint8_t' self.offset = "" store_res = ''' %(result)s = cSwap(Mem%(suffix)s, isBigEndian64(xc->tcBase())); ''' store_res = store_res % {"result":self.res, "suffix":self.suffix} self.codeBlobs["postacc_code"] = \ store_res + " SevMailbox = 1; LLSCLock = 0;" def emit(self, op): self.buildEACode() usrDecl = "%(type)s valRs;\n" % {'type': self.tp} self.codeBlobs['usrDecl'] = usrDecl opcode = "valRs = cSwap(%(dest)s,"\ " isBigEndian64(xc->tcBase()));\n" opcode += "TypedAtomicOpFunctor<%(type)s> *amo_op = "\ "new AtomicGeneric3Op<%(type)s>(Mem%(suffix)s,"\ " valRs, [](%(type)s* b, %(type)s a,"\ " %(type)s c){ %(op)s });\n" opcode = opcode % {"suffix": self.suffix, "type": self.tp , "dest": self.des, "op": op} self.codeBlobs['amo_code'] = opcode accCode = "Mem%(suffix)s = cSwap(%(result)s,"\ " isBigEndian64(xc->tcBase()));" accCode = accCode % { "result": self.res, "type":self.tp, "suffix": self.suffix} self.codeBlobs["memacc_code"] = accCode self.emitHelper(self.base) AtomicSingleOp("cas", "CAS64", 8, unsign=True, flavor="normal").emit(OP_DICT['CAS']) AtomicSingleOp("casa", "CASA64", 8, unsign=True, flavor="acquire").emit(OP_DICT['CAS']) AtomicSingleOp("casal", "CASAL64", 8, unsign=True, flavor="acquire_release").emit(OP_DICT['CAS']) AtomicSingleOp("casl", "CASL64", 8, unsign=True, flavor="release").emit(OP_DICT['CAS']) AtomicSingleOp("casb", "CASB", 1, unsign=True, flavor="normal").emit(OP_DICT['CAS']) AtomicSingleOp("casab", "CASAB", 1, unsign=True, flavor="acquire").emit(OP_DICT['CAS']) AtomicSingleOp("casalb", "CASALB", 1, unsign=True, flavor="acquire_release").emit(OP_DICT['CAS']) AtomicSingleOp("caslb", "CASLB", 1, unsign=True, flavor="release").emit(OP_DICT['CAS']) AtomicSingleOp("cash", "CASH", 2, unsign=True, flavor="normal").emit(OP_DICT['CAS']) AtomicSingleOp("casah", "CASAH", 2, unsign=True, flavor="acquire").emit(OP_DICT['CAS']) AtomicSingleOp("casalh", "CASALH", 2, unsign=True, flavor="acquire_release").emit(OP_DICT['CAS']) AtomicSingleOp("caslh", "CASLH", 2, unsign=True, flavor="release").emit(OP_DICT['CAS']) AtomicSingleOp("cas", "CAS32", 4, unsign=True, flavor="normal").emit(OP_DICT['CAS']) AtomicSingleOp("casa", "CASA32", 4, unsign=True, flavor="acquire").emit(OP_DICT['CAS']) AtomicSingleOp("casal", "CASAL32", 4, unsign=True, flavor="acquire_release").emit(OP_DICT['CAS']) AtomicSingleOp("casl", "CASL32", 4, unsign=True, flavor="release").emit(OP_DICT['CAS']) class CasPair64(AtomicInst64): decConstBase = 'AmoPairOp' base = 'ArmISA::MemoryEx64' writeback = True post = False execBase = 'AmoOp' def __init__(self, *args, **kargs): super(CasPair64, self).__init__(*args, **kargs) self.paired = True self.offset = "" if self.size == 8: self.res = 'XResult_ud' self.des = 'XDest_ud' self.tp = 'std::array' self.suffix = "_tud" store_res = ''' %(result)s = cSwap(Mem%(suffix)s[0], isBigEndian64(xc->tcBase())); uint64_t result2 = cSwap(Mem%(suffix)s[1], isBigEndian64(xc->tcBase())); xc->setIntRegOperand(this, r2_dst, (result2) & mask(aarch64 ? 64 : 32)); ''' elif self.size == 4: self.res = 'Result_uw' self.des = 'WDest_uw' self.tp = 'uint64_t' self.suffix = "_ud" store_res = ''' uint64_t data = cSwap(Mem%(suffix)s, isBigEndian64(xc->tcBase())); %(result)s = isBigEndian64(xc->tcBase()) ? (data >> 32) : (uint32_t)data; uint32_t result2 = isBigEndian64(xc->tcBase()) ? (uint32_t)data : (data >> 32); xc->setIntRegOperand(this, r2_dst, (result2) & mask(aarch64 ? 64 : 32)); ''' store_res = store_res % {"result":self.res, "suffix":self.suffix} usrDecl = "%(type)s valRs;\n" % {'type': self.tp} self.codeBlobs['usrDecl'] = usrDecl self.codeBlobs["postacc_code"] = \ store_res + " SevMailbox = 1; LLSCLock = 0;" def emit(self): self.buildEACode() # Code that actually handles the access if self.size == 4: accCode = \ "uint32_t result2 = ((xc->readIntRegOperand(this, r2_src))"\ " & mask(aarch64 ? 64 : 32)) ;\n"\ " uint32_t dest2 = ((xc->readIntRegOperand(this, d2_src)) "\ " & mask(aarch64 ? 64 : 32)) ;" accCode += ''' uint64_t data = dest2; data = isBigEndian64(xc->tcBase()) ? ((uint64_t(WDest_uw) << 32) | data) : ((data << 32) | WDest_uw); valRs = cSwap(data, isBigEndian64(xc->tcBase())); uint64_t data2 = result2 ; data2 = isBigEndian64(xc->tcBase()) ? ((uint64_t(Result_uw) << 32) | data2) : ((data2 << 32) | Result_uw); Mem_ud = cSwap(data2, isBigEndian64(xc->tcBase())); ''' opcode = "TypedAtomicOpFunctor<%(type)s> *amo_op = "\ "new AtomicGeneric3Op<%(type)s>(Mem%(suffix)s,"\ " valRs, [](%(type)s* b, %(type)s a,"\ " %(type)s c){ %(op)s });\n" elif self.size == 8: accCode = ""\ "uint64_t result2 = ((xc->readIntRegOperand(this, r2_src))"\ " & mask(aarch64 ? 64 : 32)) ;\n"\ " uint64_t dest2 = ((xc->readIntRegOperand(this, d2_src)) "\ " & mask(aarch64 ? 64 : 32)) ;" accCode += ''' // This temporary needs to be here so that the parser // will correctly identify this instruction as a store. std::array temp; temp[0] = cSwap(XDest_ud,isBigEndian64(xc->tcBase())); temp[1] = cSwap(dest2,isBigEndian64(xc->tcBase())); valRs = temp; std::array temp2; temp2[0] = cSwap(XResult_ud,isBigEndian64(xc->tcBase())); temp2[1] = cSwap(result2,isBigEndian64(xc->tcBase())); Mem_tud = temp2; ''' opcode = "TypedAtomicOpFunctor *amo_op = "\ "new AtomicGenericPair3Op(Mem_tud, "\ "valRs, [](uint64_t* b, std::array a,"\ ''' std::array c){ if(a[0]==b[0] && a[1]==b[1]){ b[0] = c[0]; b[1] = c[1]; } });''' opcode = opcode % { "suffix" : self.suffix, "type": self.tp, "op": OP_DICT['CAS']} self.codeBlobs['amo_code'] = opcode self.codeBlobs["memacc_code"] = accCode % {"type": self.tp} # Push it out to the output files self.emitHelper(self.base) CasPair64("casp", "CASP64", 8, flavor="normal", paired=True).emit() CasPair64("caspa", "CASPA64", 8, flavor="acquire", paired=True).emit() CasPair64("caspal", "CASPAL64", 8, flavor="acquire_release", paired=True).emit() CasPair64("caspl", "CASPL64", 8, flavor="release", paired=True).emit() CasPair64("casp", "CASP32", 4, flavor="normal", paired=True).emit() CasPair64("caspa", "CASPA32", 4, flavor="acquire", paired=True).emit() CasPair64("caspal", "CASPAL32", 4, flavor="acquire_release", paired=True).emit() CasPair64("caspl", "CASPL32", 4, flavor="release", paired=True).emit() }};