bug #672: pospopcount using sv.lbzu/pi/dw=8 error
[openpower-isa.git] / src / openpower / decoder / isa / caller.py
index 0c5199e2397471c78c8886d07b5983b42f361658..44c7a41e1e863df60dd38288892bc56716761f18 100644 (file)
@@ -18,6 +18,7 @@ from copy import deepcopy
 from functools import wraps
 import os
 import sys
+from elftools.elf.elffile import ELFFile  # for isinstance
 
 from nmigen.sim import Settle
 import openpower.syscalls
@@ -25,7 +26,7 @@ from openpower.consts import (MSRb, PIb,  # big-endian (PowerISA versions)
                               SVP64CROffs, SVP64MODEb)
 from openpower.decoder.helpers import (ISACallerHelper, ISAFPHelpers, exts,
                                        gtu, undefined, copy_assign_rhs)
-from openpower.decoder.isa.mem import Mem, MemMMap, MemException
+from openpower.decoder.isa.mem import Mem, MemMMap, MemException, LoadedELF
 from openpower.decoder.isa.radixmmu import RADIX
 from openpower.decoder.isa.svshape import SVSHAPE
 from openpower.decoder.isa.svstate import SVP64State
@@ -216,9 +217,13 @@ class GPR(dict):
             rnum = rnum.value
         dict.__setitem__(self, rnum, value)
 
-    def getz(self, rnum):
+    def getz(self, rnum, rvalue=None):
         # rnum = rnum.value # only SelectableInt allowed
-        log("GPR getzero?", rnum)
+        log("GPR getzero?", rnum, rvalue)
+        if rvalue is not None:
+            if rnum == 0:
+                return SelectableInt(0, rvalue.bits)
+            return rvalue
         if rnum == 0:
             return SelectableInt(0, 64)
         return self[rnum]
@@ -245,13 +250,14 @@ class GPR(dict):
                 for j in range(8):
                     s.append("%08x" % res[i+j])
                 s = ' '.join(s)
-                print("reg", "%2d" % i, s)
+                log("reg", "%2d" % i, s, kind=LogType.InstrInOuts)
         return res
 
 
 class SPR(dict):
-    def __init__(self, dec2, initial_sprs={}):
+    def __init__(self, dec2, initial_sprs={}, gpr=None):
         self.sd = dec2
+        self.gpr = gpr  # for SVSHAPE[0-3]
         dict.__init__(self)
         for key, v in initial_sprs.items():
             if isinstance(key, SelectableInt):
@@ -303,6 +309,8 @@ class SPR(dict):
             self.__setitem__('SRR1', value)
         if key == 1:
             value = XERState(value)
+        if key in ('SVSHAPE0', 'SVSHAPE1', 'SVSHAPE2', 'SVSHAPE3'):
+            value = SVSHAPE(value, self.gpr)
         log("setting spr", key, value)
         dict.__setitem__(self, key, value)
 
@@ -1141,6 +1149,10 @@ class StepLoop:
         log("    new dststep", dststep)
 
 
+class ExitSyscallCalled(Exception):
+    pass
+
+
 class SyscallEmulator(openpower.syscalls.Dispatcher):
     def __init__(self, isacaller):
         self.__isacaller = isacaller
@@ -1155,6 +1167,17 @@ class SyscallEmulator(openpower.syscalls.Dispatcher):
         (identifier, *arguments) = map(int, (identifier, *arguments))
         return super().__call__(identifier, *arguments)
 
+    def sys_exit_group(self, status, *rest):
+        self.__isacaller.halted = True
+        raise ExitSyscallCalled(status)
+
+    def sys_write(self, fd, buf, count, *rest):
+        buf = self.__isacaller.mem.get_ctypes(buf, count, is_write=False)
+        try:
+            return os.write(fd, buf)
+        except OSError as e:
+            return -e.errno
+
 
 class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
     # decoder2 - an instance of power_decoder2
@@ -1175,7 +1198,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
                  initial_fpscr=0,
                  insnlog=None,
                  use_mmap_mem=False,
-                 use_syscall_emu=False):
+                 use_syscall_emu=False,
+                 emulating_mmap=False):
         if use_syscall_emu:
             self.syscall = SyscallEmulator(isacaller=self)
             if not use_mmap_mem:
@@ -1184,6 +1208,16 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         else:
             self.syscall = None
 
+        # we will eventually be able to load ELF files without use_syscall_emu
+        # (e.g. the linux kernel), so do it in a separate if block
+        if isinstance(initial_insns, ELFFile):
+            if not use_mmap_mem:
+                log("forcing use_mmap_mem due to loading an ELF file")
+                use_mmap_mem = True
+            if not emulating_mmap:
+                log("forcing emulating_mmap due to loading an ELF file")
+                emulating_mmap = True
+
         # trace log file for model output. if None do nothing
         self.insnlog = insnlog
         self.insnlog_is_file = hasattr(insnlog, "write")
@@ -1239,23 +1273,30 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         initial_sprs = deepcopy(initial_sprs)  # so as not to get modified
         self.gpr = GPR(decoder2, self, self.svstate, regfile)
         self.fpr = GPR(decoder2, self, self.svstate, fpregfile)
-        self.spr = SPR(decoder2, initial_sprs)  # initialise SPRs before MMU
+        # initialise SPRs before MMU
+        self.spr = SPR(decoder2, initial_sprs, gpr=self.gpr)
 
         # set up 4 dummy SVSHAPEs if they aren't already set up
         for i in range(4):
             sname = 'SVSHAPE%d' % i
             val = self.spr.get(sname, 0)
-            # make sure it's an SVSHAPE
-            self.spr[sname] = SVSHAPE(val, self.gpr)
+            # make sure it's an SVSHAPE -- conversion done by SPR.__setitem__
+            self.spr[sname] = val
         self.last_op_svshape = False
 
         # "raw" memory
         if use_mmap_mem:
             self.mem = MemMMap(row_bytes=8,
                                initial_mem=initial_mem,
-                               misaligned_ok=True)
+                               misaligned_ok=True,
+                               emulating_mmap=emulating_mmap)
             self.imem = self.mem
-            self.mem.initialize(row_bytes=4, initial_mem=initial_insns)
+            lelf = self.mem.initialize(row_bytes=4, initial_mem=initial_insns)
+            if isinstance(lelf, LoadedELF):  # stuff parsed from ELF
+                initial_pc = lelf.pc
+                for k, v in lelf.gprs.items():
+                    self.gpr[k] = SelectableInt(v, 64)
+                initial_fpscr = lelf.fpscr
             self.mem.log_fancy(kind=LogType.InstrInOuts)
         else:
             self.mem = Mem(row_bytes=8, initial_mem=initial_mem,
@@ -1357,6 +1398,11 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         TRAP function is callable from inside the pseudocode itself,
         hence the default arguments.  when calling from inside ISACaller
         it is best to use call_trap()
+
+        trap_addr: int | SelectableInt
+            the address to go to (before any modifications from `KAIVB`)
+        trap_bit: int | None
+            the bit in `SRR1` to set, `None` means don't set any bits.
         """
         if isinstance(trap_addr, SelectableInt):
             trap_addr = trap_addr.value
@@ -1452,26 +1498,30 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         self.cr_backup = self.cr.value
 
         # sv.bc* need some extra fields
-        if self.is_svp64_mode and insn_name.startswith("sv.bc"):
-            # blegh grab bits manually
-            mode = yield self.dec2.rm_dec.rm_in.mode
-            # convert to SelectableInt before test
-            mode = SelectableInt(mode, 5)
-            bc_vlset = mode[SVP64MODEb.BC_VLSET] != 0
-            bc_vli = mode[SVP64MODEb.BC_VLI] != 0
-            bc_snz = mode[SVP64MODEb.BC_SNZ] != 0
-            bc_vsb = yield self.dec2.rm_dec.bc_vsb
-            bc_lru = yield self.dec2.rm_dec.bc_lru
-            bc_gate = yield self.dec2.rm_dec.bc_gate
-            sz = yield self.dec2.rm_dec.pred_sz
-            self.namespace['mode'] = SelectableInt(mode, 5)
-            self.namespace['ALL'] = SelectableInt(bc_gate, 1)
-            self.namespace['VSb'] = SelectableInt(bc_vsb, 1)
-            self.namespace['LRu'] = SelectableInt(bc_lru, 1)
-            self.namespace['VLSET'] = SelectableInt(bc_vlset, 1)
-            self.namespace['VLI'] = SelectableInt(bc_vli, 1)
-            self.namespace['sz'] = SelectableInt(sz, 1)
-            self.namespace['SNZ'] = SelectableInt(bc_snz, 1)
+        if not self.is_svp64_mode or not insn_name.startswith("sv.bc"):
+            return
+
+        # blegh grab bits manually
+        mode = yield self.dec2.rm_dec.rm_in.mode
+        # convert to SelectableInt before test
+        mode = SelectableInt(mode, 5)
+        bc_vlset = mode[SVP64MODEb.BC_VLSET] != 0
+        bc_vli = mode[SVP64MODEb.BC_VLI] != 0
+        bc_snz = mode[SVP64MODEb.BC_SNZ] != 0
+        bc_vsb = yield self.dec2.rm_dec.bc_vsb
+        bc_ctrtest = yield self.dec2.rm_dec.bc_ctrtest
+        bc_lru = yield self.dec2.rm_dec.bc_lru
+        bc_gate = yield self.dec2.rm_dec.bc_gate
+        sz = yield self.dec2.rm_dec.pred_sz
+        self.namespace['mode'] = SelectableInt(mode, 5)
+        self.namespace['ALL'] = SelectableInt(bc_gate, 1)
+        self.namespace['VSb'] = SelectableInt(bc_vsb, 1)
+        self.namespace['LRu'] = SelectableInt(bc_lru, 1)
+        self.namespace['CTRtest'] = SelectableInt(bc_ctrtest, 1)
+        self.namespace['VLSET'] = SelectableInt(bc_vlset, 1)
+        self.namespace['VLI'] = SelectableInt(bc_vli, 1)
+        self.namespace['sz'] = SelectableInt(sz, 1)
+        self.namespace['SNZ'] = SelectableInt(bc_snz, 1)
 
     def get_kludged_op_add_ca_ov(self, inputs, inp_ca_ov):
         """ this was not at all necessary to do.  this function massively
@@ -1564,6 +1614,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         return ca64, ca32, ov64, ov32
 
     def handle_carry_(self, inputs, output, ca, ca32, inp_ca_ov):
+        if ca is not None and ca32 is not None:
+            return
         op = yield self.dec2.e.do.insn_type
         if op == MicrOp.OP_ADD.value and ca is None and ca32 is None:
             retval = yield from self.get_kludged_op_add_ca_ov(
@@ -2004,8 +2056,6 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         # 2. Call the HDL implementation which invokes trap.
         # 3. Reroute the guest system call to host system call.
         # 4. Force return from the interrupt as if we had guest OS.
-        #    "executing" rfid requires putting 0x4c000024 temporarily
-        #    into the program at the PC. TODO investigate and remove
         if ((asmop in ("sc", "scv")) and
                 (self.syscall is not None) and
                 not syscall_emu_active):
@@ -2023,10 +2073,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             self.gpr.write(3, result, False, self.namespace["XLEN"])
 
             # Return from interrupt
-            backup = self.imem.ld(pc, 4, False, True, instr_fetch=True)
-            self.imem.st(pc, 0x4c000024, width=4, swap=True)
             yield from self.call("rfid", syscall_emu_active=True)
-            self.imem.st(pc, backup, width=4, swap=True)
+            return
         elif ((name in ("rfid", "hrfid")) and syscall_emu_active):
             asmop = "rfid"
 
@@ -2044,7 +2092,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
                        "brh", "brw", "brd",
                        'setvl', 'svindex', 'svremap', 'svstep',
                        'svshape', 'svshape2',
-                       'ternlogi', 'bmask', 'cprop',
+                       'ternlogi', 'bmask', 'cprop', 'gbbd',
                        'absdu', 'absds', 'absdacs', 'absdacu', 'avgadd',
                        'fmvis', 'fishmv', 'pcdec', "maddedu", "divmod2du",
                        "dsld", "dsrd", "maddedus",
@@ -2060,6 +2108,11 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             illegal = False
             ins_name = dotstrp
 
+        # match against instructions treated as nop, see nop below
+        if asmop.startswith("dcbt"):
+            illegal = False
+            ins_name = "nop"
+
         # branch-conditional redirects to sv.bc
         if asmop.startswith('bc') and self.is_svp64_mode:
             ins_name = 'sv.%s' % ins_name
@@ -2105,7 +2158,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             ew_src = 8 << (3-int(ew_src))  # convert to bitlength
             ew_dst = 8 << (3-int(ew_dst))  # convert to bitlength
             xlen = max(ew_src, ew_dst)
-            log("elwdith", ew_src, ew_dst)
+            log("elwidth", ew_src, ew_dst)
         log("XLEN:", self.is_svp64_mode, xlen)
 
         # look up instruction in ISA.instrs, prepare namespace
@@ -2186,7 +2239,16 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             remap_active = yield self.dec2.remap_active
         else:
             remap_active = False
-        log("remap active", bin(remap_active))
+        log("remap active", bin(remap_active), self.is_svp64_mode)
+
+        # LDST does *not* allow elwidth overrides on RA (Effective Address).
+        # this has to be detected. XXX TODO: RB for ldst-idx *may* need
+        # conversion (to 64-bit) also.
+        # see write reg this *HAS* to also override XLEN to 64 on LDST/Update
+        sv_mode = yield self.dec2.rm_dec.sv_mode
+        is_ldst = (sv_mode in [SVMode.LDST_IDX.value, SVMode.LDST_IMM.value] \
+                  and self.is_svp64_mode)
+        log("is_ldst", sv_mode, is_ldst)
 
         # main input registers (RT, RA ...)
         for name in input_names:
@@ -2200,8 +2262,12 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
                 inputs[name] = self.crl[0]
             elif name in spr_byname:
                 inputs[name] = self.spr[name]
+            elif is_ldst and name == 'RA':
+                regval = (yield from self.get_input(name, ew_src, 64))
+                log("EA (RA) regval name", name, regval)
+                inputs[name] = regval
             else:
-                regval = (yield from self.get_input(name, ew_src))
+                regval = (yield from self.get_input(name, ew_src, xlen))
                 log("regval name", name, regval)
                 inputs[name] = regval
 
@@ -2233,10 +2299,11 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
 
         # check if this was an sv.bc* and create an indicator that
         # this is the last check to be made as a loop.  combined with
-        # the ALL/ANY mode we can early-exit
+        # the ALL/ANY mode we can early-exit. note that BI (to test)
+        # is an input so there is no termination if BI is scalar
+        # (because early-termination is for *output* scalars)
         if self.is_svp64_mode and ins_name.startswith("sv.bc"):
-            no_in_vec = yield self.dec2.no_in_vec  # BI is scalar
-            end_loop = no_in_vec or srcstep == vl-1 or dststep == vl-1
+            end_loop = srcstep == vl-1 or dststep == vl-1
             self.namespace['end_loop'] = SelectableInt(end_loop, 1)
 
         inp_ca_ov = (self.spr['XER'][XER_bits['CA']].value,
@@ -2323,8 +2390,12 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         # XXX TODO: now that CR0 is supported, sort out svstep's pseudocode
         # to write directly to CR0 instead of in ISACaller. hooyahh.
         if rc_en and ins_name not in ['svstep']:
+            if outs_ok.get('FPSCR', False):
+                FPSCR = outs['FPSCR']
+            else:
+                FPSCR = self.FPSCR
             yield from self.do_rc_ov(
-                ins_name, results[0], overflow, cr0, cr1, output_names)
+                ins_name, results[0], overflow, cr0, cr1, FPSCR)
 
         # check failfirst
         ffirst_hit = False, False
@@ -2332,6 +2403,9 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             sv_mode = yield self.dec2.rm_dec.sv_mode
             is_cr = sv_mode == SVMode.CROP.value
             chk = rc_en or is_cr
+            if outs_ok.get('CR', False):
+                # early write so check_ffirst can see value
+                self.namespace['CR'].eq(outs['CR'])
             ffirst_hit = (yield from self.check_ffirst(info, chk, srcstep))
 
         # any modified return results?
@@ -2382,7 +2456,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         yield Settle()  # let decoder update
         return True, vli_
 
-    def do_rc_ov(self, ins_name, result, overflow, cr0, cr1, output_names):
+    def do_rc_ov(self, ins_name, result, overflow, cr0, cr1, FPSCR):
         cr_out = yield self.dec2.op.cr_out
         if cr_out == CROutSel.CR1.value:
             rc_reg = "CR1"
@@ -2398,10 +2472,10 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
 
         if rc_reg == "CR1":
             if cr1 is None:
-                cr1 = int(self.FPSCR.FX) << 3
-                cr1 |= int(self.FPSCR.FEX) << 2
-                cr1 |= int(self.FPSCR.VX) << 1
-                cr1 |= int(self.FPSCR.OX)
+                cr1 = int(FPSCR.FX) << 3
+                cr1 |= int(FPSCR.FEX) << 2
+                cr1 |= int(FPSCR.VX) << 1
+                cr1 |= int(FPSCR.OX)
                 log("default fp cr1", cr1)
             else:
                 log("explicit cr1", cr1)
@@ -2437,8 +2511,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             nia_update = True
         else:
             # check advancement of src/dst/sub-steps and if PC needs updating
-            nia_update = (yield from self.check_step_increment(rc_en,
-                                                               asmop, ins_name))
+            nia_update = (yield from self.check_step_increment(
+                rc_en, asmop, ins_name))
         if nia_update:
             self.update_pc_next()
 
@@ -2493,7 +2567,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             else:
                 self.namespace['D'] = imm
 
-    def get_input(self, name, ew_src):
+    def get_input(self, name, ew_src, xlen):
         # using PowerDecoder2, first, find the decoder index.
         # (mapping name RA RB RC RS to in1, in2, in3)
         regnum, is_vec = yield from get_idx_in(self.dec2, name, True)
@@ -2514,20 +2588,27 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         regname = "_" + name
         if not self.is_svp64_mode or ew_src == 64:
             self.namespace[regname] = regnum
-        elif regname in self.namespace:
-            del self.namespace[regname]
+        else:
+            # FIXME: we're trying to access a sub-register, plain register
+            # numbers don't work for that.  for now, just pass something that
+            # can be compared to 0 and probably will cause an error if misused.
+            # see https://bugs.libre-soc.org/show_bug.cgi?id=1221
+            self.namespace[regname] = regnum * 10000
 
         if not self.is_svp64_mode or not self.pred_src_zero:
             log('reading reg %s %s' % (name, str(regnum)), is_vec)
             if name in fregs:
-                reg_val = SelectableInt(self.fpr(base, is_vec, offs, ew_src))
-                log("read reg %d/%d: 0x%x" % (base, offs, reg_val.value),
-                    kind=LogType.InstrInOuts)
+                fval = self.fpr(base, is_vec, offs, ew_src)
+                reg_val = SelectableInt(fval)
+                assert ew_src == XLEN, "TODO fix elwidth conversion"
                 self.trace("r:FPR:%d:%d:%d " % (base, offs, ew_src))
+                log("read fp reg %d/%d: 0x%x" % (base, offs, reg_val.value),
+                    kind=LogType.InstrInOuts)
             elif name is not None:
-                reg_val = SelectableInt(self.gpr(base, is_vec, offs, ew_src))
+                gval = self.gpr(base, is_vec, offs, ew_src)
+                reg_val = SelectableInt(gval.value, bits=xlen)
                 self.trace("r:GPR:%d:%d:%d " % (base, offs, ew_src))
-                log("read reg %d/%d: 0x%x" % (base, offs, reg_val.value),
+                log("read int reg %d/%d: 0x%x" % (base, offs, reg_val.value),
                     kind=LogType.InstrInOuts)
         else:
             log('zero input reg %s %s' % (name, str(regnum)), is_vec)
@@ -2656,9 +2737,24 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         if name in fregs:
             self.fpr.write(regnum, output, is_vec, ew_dst)
             self.trace("w:FPR:%d:%d:%d " % (rnum, offset, ew_dst))
-        else:
-            self.gpr.write(regnum, output, is_vec, ew_dst)
-            self.trace("w:GPR:%d:%d:%d " % (rnum, offset, ew_dst))
+            return
+
+        # LDST/Update does *not* allow elwidths on RA (Effective Address).
+        # this has to be detected, and overridden.  see get_input (related)
+        sv_mode = yield self.dec2.rm_dec.sv_mode
+        is_ldst = (sv_mode in [SVMode.LDST_IDX.value, SVMode.LDST_IMM.value] \
+                  and self.is_svp64_mode)
+        if is_ldst and name in ['EA', 'RA']:
+            op = self.dec2.dec.op
+            if hasattr(op, "upd"):
+                # update mode LD/ST uses read-reg A also as an output
+                upd = yield op.upd
+                log("write is_ldst is_update", sv_mode, is_ldst, upd)
+                if upd == LDSTMode.update.value:
+                    ew_dst = 64 # override for RA (EA) to 64-bit
+
+        self.gpr.write(regnum, output, is_vec, ew_dst)
+        self.trace("w:GPR:%d:%d:%d " % (rnum, offset, ew_dst))
 
     def check_step_increment(self, rc_en, asmop, ins_name):
         # check if it is the SVSTATE.src/dest step that needs incrementing
@@ -2817,6 +2913,9 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         vfirst = self.svstate.vfirst
         log("    SV Vertical First", vf, vfirst)
         if not vf and vfirst == 1:
+            if insn_name.startswith("sv.bc"):
+                self.update_pc_next()
+                return False
             self.update_nia()
             return True
 
@@ -2883,7 +2982,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         # not an SVP64 branch, so fix PC (NIA==CIA) for next loop
         # (by default, NIA is CIA+4 if v3.0B or CIA+8 if SVP64)
         # this way we keep repeating the same instruction (with new steps)
-        self.pc.NIA.value = self.pc.CIA.value
+        self.pc.NIA.eq(self.pc.CIA)
         self.namespace['NIA'] = self.pc.NIA
         log("end of sub-pc call", self.namespace['CIA'], self.namespace['NIA'])
         return False  # DO NOT allow PC update whilst Sub-PC loop running