fix elwidth overrides when sw=8
[openpower-isa.git] / src / openpower / decoder / isa / caller.py
index 3b8d86948894b266ea0c71403d35bdc89fb0ac7c..9a8c24817c27f77ea51003ab2ecd1af39e271a93 100644 (file)
@@ -16,13 +16,17 @@ related bugs:
 from collections import namedtuple
 from copy import deepcopy
 from functools import wraps
 from collections import namedtuple
 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
 
 from nmigen.sim import Settle
+import openpower.syscalls
 from openpower.consts import (MSRb, PIb,  # big-endian (PowerISA versions)
                               SVP64CROffs, SVP64MODEb)
 from openpower.decoder.helpers import (ISACallerHelper, ISAFPHelpers, exts,
 from openpower.consts import (MSRb, PIb,  # big-endian (PowerISA versions)
                               SVP64CROffs, SVP64MODEb)
 from openpower.decoder.helpers import (ISACallerHelper, ISAFPHelpers, exts,
-                                       gtu, undefined)
-from openpower.decoder.isa.mem import Mem, MemException
+                                       gtu, undefined, copy_assign_rhs)
+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
 from openpower.decoder.isa.radixmmu import RADIX
 from openpower.decoder.isa.svshape import SVSHAPE
 from openpower.decoder.isa.svstate import SVP64State
@@ -40,9 +44,10 @@ from openpower.decoder.power_svp64 import SVP64RM, decode_extra
 from openpower.decoder.selectable_int import (FieldSelectableInt,
                                               SelectableInt, selectconcat,
                                               EFFECTIVELY_UNLIMITED)
 from openpower.decoder.selectable_int import (FieldSelectableInt,
                                               SelectableInt, selectconcat,
                                               EFFECTIVELY_UNLIMITED)
+from openpower.consts import DEFAULT_MSR
 from openpower.fpscr import FPSCRState
 from openpower.xer import XERState
 from openpower.fpscr import FPSCRState
 from openpower.xer import XERState
-from openpower.util import LogKind, log
+from openpower.util import LogType, log
 
 LDST_UPDATE_INSNS = ['ldu', 'lwzu', 'lbzu', 'lhzu', 'lhau', 'lfsu', 'lfdu',
                      'stwu', 'stbu', 'sthu', 'stfsu', 'stfdu', 'stdu',
 
 LDST_UPDATE_INSNS = ['ldu', 'lwzu', 'lbzu', 'lhzu', 'lhau', 'lfsu', 'lfdu',
                      'stwu', 'stbu', 'sthu', 'stfsu', 'stfdu', 'stdu',
@@ -140,6 +145,12 @@ def create_args(reglist, extra=None):
     return retval
 
 
     return retval
 
 
+def create_full_args(*, read_regs, special_regs, uninit_regs, write_regs,
+                     extra=None):
+    return create_args([
+        *read_regs, *uninit_regs, *write_regs, *special_regs], extra=extra)
+
+
 class GPR(dict):
     def __init__(self, decoder, isacaller, svstate, regfile):
         dict.__init__(self)
 class GPR(dict):
     def __init__(self, decoder, isacaller, svstate, regfile):
         dict.__init__(self)
@@ -235,13 +246,14 @@ class GPR(dict):
                 for j in range(8):
                     s.append("%08x" % res[i+j])
                 s = ' '.join(s)
                 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):
         return res
 
 
 class SPR(dict):
-    def __init__(self, dec2, initial_sprs={}):
+    def __init__(self, dec2, initial_sprs={}, gpr=None):
         self.sd = dec2
         self.sd = dec2
+        self.gpr = gpr  # for SVSHAPE[0-3]
         dict.__init__(self)
         for key, v in initial_sprs.items():
             if isinstance(key, SelectableInt):
         dict.__init__(self)
         for key, v in initial_sprs.items():
             if isinstance(key, SelectableInt):
@@ -293,6 +305,8 @@ class SPR(dict):
             self.__setitem__('SRR1', value)
         if key == 1:
             value = XERState(value)
             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)
 
         log("setting spr", key, value)
         dict.__setitem__(self, key, value)
 
@@ -569,9 +583,9 @@ def get_cr_out(dec2, name):
     if RC1:
         log("get_cr_out RC1 mode")
         if name == 'CR0':
     if RC1:
         log("get_cr_out RC1 mode")
         if name == 'CR0':
-            return 0, True # XXX TODO: offset CR0 from SVSTATE SPR
+            return 0, True  # XXX TODO: offset CR0 from SVSTATE SPR
         if name == 'CR1':
         if name == 'CR1':
-            return 1, True # XXX TODO: offset CR1 from SVSTATE SPR
+            return 1, True  # XXX TODO: offset CR1 from SVSTATE SPR
     # nope - not found.
     log("get_cr_out not found", name)
     return None, False
     # nope - not found.
     log("get_cr_out not found", name)
     return None, False
@@ -1131,6 +1145,36 @@ class StepLoop:
         log("    new dststep", dststep)
 
 
         log("    new dststep", dststep)
 
 
+class ExitSyscallCalled(Exception):
+    pass
+
+
+class SyscallEmulator(openpower.syscalls.Dispatcher):
+    def __init__(self, isacaller):
+        self.__isacaller = isacaller
+
+        host = os.uname().machine
+        bits = (64 if (sys.maxsize > (2**32)) else 32)
+        host = openpower.syscalls.architecture(arch=host, bits=bits)
+
+        return super().__init__(guest="ppc64", host=host)
+
+    def __call__(self, identifier, *arguments):
+        (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
     # regfile - a list of initial values for the registers
 class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
     # decoder2 - an instance of power_decoder2
     # regfile - a list of initial values for the registers
@@ -1148,7 +1192,27 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
                  mmu=False,
                  icachemmu=False,
                  initial_fpscr=0,
                  mmu=False,
                  icachemmu=False,
                  initial_fpscr=0,
-                 insnlog=None):
+                 insnlog=None,
+                 use_mmap_mem=False,
+                 use_syscall_emu=False,
+                 emulating_mmap=False):
+        if use_syscall_emu:
+            self.syscall = SyscallEmulator(isacaller=self)
+            if not use_mmap_mem:
+                log("forcing use_mmap_mem due to use_syscall_emu active")
+                use_mmap_mem = True
+        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
 
         # trace log file for model output. if None do nothing
         self.insnlog = insnlog
@@ -1169,6 +1233,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         if initial_insns is None:
             initial_insns = {}
             assert self.respect_pc == False, "instructions required to honor pc"
         if initial_insns is None:
             initial_insns = {}
             assert self.respect_pc == False, "instructions required to honor pc"
+        if initial_msr is None:
+            initial_msr = DEFAULT_MSR
 
         log("ISACaller insns", respect_pc, initial_insns, disassembly)
         log("ISACaller initial_msr", initial_msr)
 
         log("ISACaller insns", respect_pc, initial_insns, disassembly)
         log("ISACaller initial_msr", initial_msr)
@@ -1203,20 +1269,36 @@ 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)
         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)
 
         # 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
         self.last_op_svshape = False
 
         # "raw" memory
-        self.mem = Mem(row_bytes=8, initial_mem=initial_mem, misaligned_ok=True)
-        self.mem.log_fancy(kind=LogKind.InstrInOuts)
-        self.imem = Mem(row_bytes=4, initial_mem=initial_insns)
+        if use_mmap_mem:
+            self.mem = MemMMap(row_bytes=8,
+                               initial_mem=initial_mem,
+                               misaligned_ok=True,
+                               emulating_mmap=emulating_mmap)
+            self.imem = self.mem
+            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,
+                           misaligned_ok=True)
+            self.mem.log_fancy(kind=LogType.InstrInOuts)
+            self.imem = Mem(row_bytes=4, initial_mem=initial_insns)
         # MMU mode, redirect underlying Mem through RADIX
         if mmu:
             self.mem = RADIX(self.mem, self)
         # MMU mode, redirect underlying Mem through RADIX
         if mmu:
             self.mem = RADIX(self.mem, self)
@@ -1286,7 +1368,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         super().__init__(XLEN=self.namespace["XLEN"], FPSCR=self.fpscr)
 
     def trace(self, out):
         super().__init__(XLEN=self.namespace["XLEN"], FPSCR=self.fpscr)
 
     def trace(self, out):
-        if self.insnlog is None: return
+        if self.insnlog is None:
+            return
         self.insnlog.write(out)
 
     @property
         self.insnlog.write(out)
 
     @property
@@ -1311,7 +1394,14 @@ 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 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
         # https://bugs.libre-soc.org/show_bug.cgi?id=859
         kaivb = self.spr['KAIVB'].value
         msr = self.namespace['MSR'].value
         # https://bugs.libre-soc.org/show_bug.cgi?id=859
         kaivb = self.spr['KAIVB'].value
         msr = self.namespace['MSR'].value
@@ -1324,7 +1414,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         if self.is_svp64_mode:
             self.spr['SVSRR0'] = self.namespace['SVSTATE'].value
         self.trap_nia = SelectableInt(trap_addr | (kaivb & ~0x1fff), 64)
         if self.is_svp64_mode:
             self.spr['SVSRR0'] = self.namespace['SVSTATE'].value
         self.trap_nia = SelectableInt(trap_addr | (kaivb & ~0x1fff), 64)
-        self.spr['SRR1'][trap_bit] = 1  # change *copy* of MSR in SRR1
+        if trap_bit is not None:
+            self.spr['SRR1'][trap_bit] = 1  # change *copy* of MSR in SRR1
 
         # set exception bits.  TODO: this should, based on the address
         # in figure 66 p1065 V3.0B and the table figure 65 p1063 set these
 
         # set exception bits.  TODO: this should, based on the address
         # in figure 66 p1065 V3.0B and the table figure 65 p1063 set these
@@ -1403,26 +1494,30 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         self.cr_backup = self.cr.value
 
         # sv.bc* need some extra fields
         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
 
     def get_kludged_op_add_ca_ov(self, inputs, inp_ca_ov):
         """ this was not at all necessary to do.  this function massively
@@ -1515,6 +1610,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         return ca64, ca32, ov64, ov32
 
     def handle_carry_(self, inputs, output, ca, ca32, inp_ca_ov):
         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(
         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(
@@ -1525,10 +1622,14 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
                 # TODO: if 32-bit mode, set ov to ov32
                 self.spr['XER'][XER_bits['OV']] = ov
                 self.spr['XER'][XER_bits['OV32']] = ov32
                 # TODO: if 32-bit mode, set ov to ov32
                 self.spr['XER'][XER_bits['OV']] = ov
                 self.spr['XER'][XER_bits['OV32']] = ov32
+                log(f"write OV/OV32 OV={ov} OV32={ov32}",
+                    kind=LogType.InstrInOuts)
             else:
                 # TODO: if 32-bit mode, set ca to ca32
                 self.spr['XER'][XER_bits['CA']] = ca
                 self.spr['XER'][XER_bits['CA32']] = ca32
             else:
                 # TODO: if 32-bit mode, set ca to ca32
                 self.spr['XER'][XER_bits['CA']] = ca
                 self.spr['XER'][XER_bits['CA32']] = ca32
+                log(f"write CA/CA32 CA={ca} CA32={ca32}",
+                    kind=LogType.InstrInOuts)
             return
         inv_a = yield self.dec2.e.do.invert_in
         if inv_a:
             return
         inv_a = yield self.dec2.e.do.invert_in
         if inv_a:
@@ -1645,10 +1746,10 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         else:
             SO = self.spr['XER'][XER_bits['SO']]
         log("handle_comparison SO", SO.value,
         else:
             SO = self.spr['XER'][XER_bits['SO']]
         log("handle_comparison SO", SO.value,
-                    "overflow", overflow,
-                    "zero", zero.value,
-                    "+ve", positive.value,
-                     "-ve", negative.value)
+            "overflow", overflow,
+            "zero", zero.value,
+            "+ve", positive.value,
+            "-ve", negative.value)
         # alternative overflow checking (setvl mainly at the moment)
         if overflow is not None and overflow == 1:
             SO = SelectableInt(1, 1)
         # alternative overflow checking (setvl mainly at the moment)
         if overflow is not None and overflow == 1:
             SO = SelectableInt(1, 1)
@@ -1898,7 +1999,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         dec_insn = yield self.dec2.e.do.insn
         return dec_insn & (1 << 20) != 0  # sigh - XFF.spr[-1]?
 
         dec_insn = yield self.dec2.e.do.insn
         return dec_insn & (1 << 20) != 0  # sigh - XFF.spr[-1]?
 
-    def call(self, name):
+    def call(self, name, syscall_emu_active=False):
         """call(opcode) - the primary execution point for instructions
         """
         self.last_st_addr = None  # reset the last known store address
         """call(opcode) - the primary execution point for instructions
         """
         self.last_st_addr = None  # reset the last known store address
@@ -1912,7 +2013,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         # TODO, asmregs is from the spec, e.g. add RT,RA,RB
         # see http://bugs.libre-riscv.org/show_bug.cgi?id=282
         asmop = yield from self.get_assembly_name()
         # TODO, asmregs is from the spec, e.g. add RT,RA,RB
         # see http://bugs.libre-riscv.org/show_bug.cgi?id=282
         asmop = yield from self.get_assembly_name()
-        log("call", ins_name, asmop)
+        log("call", ins_name, asmop,
+            kind=LogType.InstrInOuts)
 
         # sv.setvl is *not* a loop-function. sigh
         log("is_svp64_mode", self.is_svp64_mode, asmop)
 
         # sv.setvl is *not* a loop-function. sigh
         log("is_svp64_mode", self.is_svp64_mode, asmop)
@@ -1945,6 +2047,33 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             self.halted = True
             return
 
             self.halted = True
             return
 
+        # User mode system call emulation consists of several steps:
+        # 1. Detect whether instruction is sc or scv.
+        # 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.
+        if ((asmop in ("sc", "scv")) and
+                (self.syscall is not None) and
+                not syscall_emu_active):
+            # Memoize PC and trigger an interrupt
+            if self.respect_pc:
+                pc = self.pc.CIA.value
+            else:
+                pc = self.fake_pc
+            yield from self.call(asmop, syscall_emu_active=True)
+
+            # Reroute the syscall to host OS
+            identifier = self.gpr(0)
+            arguments = map(self.gpr, range(3, 9))
+            result = self.syscall(identifier, *arguments)
+            self.gpr.write(3, result, False, self.namespace["XLEN"])
+
+            # Return from interrupt
+            yield from self.call("rfid", syscall_emu_active=True)
+            return
+        elif ((name in ("rfid", "hrfid")) and syscall_emu_active):
+            asmop = "rfid"
+
         # check illegal instruction
         illegal = False
         if ins_name not in ['mtcrf', 'mtocrf']:
         # check illegal instruction
         illegal = False
         if ins_name not in ['mtcrf', 'mtocrf']:
@@ -1959,7 +2088,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
                        "brh", "brw", "brd",
                        'setvl', 'svindex', 'svremap', 'svstep',
                        'svshape', 'svshape2',
                        "brh", "brw", "brd",
                        'setvl', 'svindex', 'svremap', 'svstep',
                        'svshape', 'svshape2',
-                       'grev', 'ternlogi', 'bmask', 'cprop',
+                       'ternlogi', 'bmask', 'cprop', 'gbbd',
                        'absdu', 'absds', 'absdacs', 'absdacu', 'avgadd',
                        'fmvis', 'fishmv', 'pcdec', "maddedu", "divmod2du",
                        "dsld", "dsrd", "maddedus",
                        'absdu', 'absds', 'absdacs', 'absdacu', 'avgadd',
                        'fmvis', 'fishmv', 'pcdec', "maddedu", "divmod2du",
                        "dsld", "dsrd", "maddedus",
@@ -1968,11 +2097,18 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
                        "mffpr", "mffprs",
                        "ctfpr", "ctfprs",
                        "mtfpr", "mtfprs",
                        "mffpr", "mffprs",
                        "ctfpr", "ctfprs",
                        "mtfpr", "mtfprs",
-                       "maddsubrs", "maddrs", "msubrs"
+                       "maddsubrs", "maddrs", "msubrs",
+                       "cfuged", "cntlzdm", "cnttzdm", "pdepd", "pextd",
+                       "setbc", "setbcr", "setnbc", "setnbcr",
                        ]:
             illegal = False
             ins_name = dotstrp
 
                        ]:
             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
         # branch-conditional redirects to sv.bc
         if asmop.startswith('bc') and self.is_svp64_mode:
             ins_name = 'sv.%s' % ins_name
@@ -2018,7 +2154,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)
             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
         log("XLEN:", self.is_svp64_mode, xlen)
 
         # look up instruction in ISA.instrs, prepare namespace
@@ -2031,9 +2167,16 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         yield from self.prep_namespace(ins_name, info.form, info.op_fields,
                                        xlen)
 
         yield from self.prep_namespace(ins_name, info.form, info.op_fields,
                                        xlen)
 
+        # dict retains order
+        inputs = dict.fromkeys(create_full_args(
+            read_regs=info.read_regs, special_regs=info.special_regs,
+            uninit_regs=info.uninit_regs, write_regs=info.write_regs))
+
         # preserve order of register names
         # preserve order of register names
-        input_names = create_args(list(info.read_regs) +
-                                  list(info.uninit_regs))
+        write_without_special_regs = OrderedSet(info.write_regs)
+        write_without_special_regs -= OrderedSet(info.special_regs)
+        input_names = create_args([
+            *info.read_regs, *info.uninit_regs, *write_without_special_regs])
         log("input names", input_names)
 
         # get SVP64 entry for the current instruction
         log("input names", input_names)
 
         # get SVP64 entry for the current instruction
@@ -2070,7 +2213,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         if self.is_svp64_mode and vl == 0:
             self.pc.update(self.namespace, self.is_svp64_mode)
             log("SVP64: VL=0, end of call", self.namespace['CIA'],
         if self.is_svp64_mode and vl == 0:
             self.pc.update(self.namespace, self.is_svp64_mode)
             log("SVP64: VL=0, end of call", self.namespace['CIA'],
-                self.namespace['NIA'], kind=LogKind.InstrInOuts)
+                self.namespace['NIA'], kind=LogType.InstrInOuts)
             return
 
         # for when SVREMAP is active, using pre-arranged schedule.
             return
 
         # for when SVREMAP is active, using pre-arranged schedule.
@@ -2095,11 +2238,21 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         log("remap active", bin(remap_active))
 
         # main input registers (RT, RA ...)
         log("remap active", bin(remap_active))
 
         # main input registers (RT, RA ...)
-        inputs = []
         for name in input_names:
         for name in input_names:
-            regval = (yield from self.get_input(name, ew_src))
-            log("regval name", name, regval)
-            inputs.append(regval)
+            if name == "overflow":
+                inputs[name] = SelectableInt(0, 1)
+            elif name == "FPSCR":
+                inputs[name] = self.FPSCR
+            elif name in ("CA", "CA32", "OV", "OV32"):
+                inputs[name] = self.spr['XER'][XER_bits[name]]
+            elif name in "CR0":
+                inputs[name] = self.crl[0]
+            elif name in spr_byname:
+                inputs[name] = self.spr[name]
+            else:
+                regval = (yield from self.get_input(name, ew_src, xlen))
+                log("regval name", name, regval)
+                inputs[name] = regval
 
         # arrrrgh, awful hack, to get _RT into namespace
         if ins_name in ['setvl', 'svstep']:
 
         # arrrrgh, awful hack, to get _RT into namespace
         if ins_name in ['setvl', 'svstep']:
@@ -2120,32 +2273,49 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         # "special" registers
         for special in info.special_regs:
             if special in special_sprs:
         # "special" registers
         for special in info.special_regs:
             if special in special_sprs:
-                inputs.append(self.spr[special])
+                inputs[special] = self.spr[special]
             else:
             else:
-                inputs.append(self.namespace[special])
+                inputs[special] = self.namespace[special]
 
         # clear trap (trap) NIA
         self.trap_nia = None
 
         # 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
 
         # clear trap (trap) NIA
         self.trap_nia = None
 
         # 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"):
         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,
                      self.spr['XER'][XER_bits['OV']].value)
 
             self.namespace['end_loop'] = SelectableInt(end_loop, 1)
 
         inp_ca_ov = (self.spr['XER'][XER_bits['CA']].value,
                      self.spr['XER'][XER_bits['OV']].value)
 
+        for k, v in inputs.items():
+            if v is None:
+                v = SelectableInt(0, self.XLEN)
+            # prevent pseudo-code from modifying input registers
+            v = copy_assign_rhs(v)
+            if isinstance(v, SelectableInt):
+                v.ok = False
+            inputs[k] = v
+
         # execute actual instruction here (finally)
         log("inputs", inputs)
         # execute actual instruction here (finally)
         log("inputs", inputs)
+        inputs = list(inputs.values())
         results = info.func(self, *inputs)
         output_names = create_args(info.write_regs)
         outs = {}
         results = info.func(self, *inputs)
         output_names = create_args(info.write_regs)
         outs = {}
+        # record .ok before anything after the pseudo-code can modify it
+        outs_ok = {}
         for out, n in zip(results or [], output_names):
             outs[n] = out
         for out, n in zip(results or [], output_names):
             outs[n] = out
+            outs_ok[n] = True
+            if isinstance(out, SelectableInt):
+                outs_ok[n] = out.ok
         log("results", outs)
         log("results", outs)
+        log("results ok", outs_ok)
 
         # "inject" decorator takes namespace from function locals: we need to
         # overwrite NIA being overwritten (sigh)
 
         # "inject" decorator takes namespace from function locals: we need to
         # overwrite NIA being overwritten (sigh)
@@ -2170,17 +2340,22 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         ca32 = outs.get("CA32")
 
         log("carry already done?", ca, ca32, output_names)
         ca32 = outs.get("CA32")
 
         log("carry already done?", ca, ca32, output_names)
-        carry_en = yield self.dec2.e.do.output_carry
+        # soc test_pipe_caller tests don't have output_carry
+        has_output_carry = hasattr(self.dec2.e.do, "output_carry")
+        carry_en = has_output_carry and (yield self.dec2.e.do.output_carry)
         if carry_en:
             yield from self.handle_carry_(
                 inputs, results[0], ca, ca32, inp_ca_ov=inp_ca_ov)
 
         if carry_en:
             yield from self.handle_carry_(
                 inputs, results[0], ca, ca32, inp_ca_ov=inp_ca_ov)
 
-        # get outout named "overflow" and "CR0"
+        # get output named "overflow" and "CR0"
         overflow = outs.get('overflow')
         cr0 = outs.get('CR0')
         cr1 = outs.get('CR1')
 
         overflow = outs.get('overflow')
         cr0 = outs.get('CR0')
         cr1 = outs.get('CR1')
 
-        if not self.is_svp64_mode:  # yeah just no. not in parallel processing
+        # soc test_pipe_caller tests don't have oe
+        has_oe = hasattr(self.dec2.e.do, "oe")
+        # yeah just no. not in parallel processing
+        if has_oe and not self.is_svp64_mode:
             # detect if overflow was in return result
             ov_en = yield self.dec2.e.do.oe.oe
             ov_ok = yield self.dec2.e.do.oe.ok
             # detect if overflow was in return result
             ov_en = yield self.dec2.e.do.oe.oe
             ov_ok = yield self.dec2.e.do.oe.ok
@@ -2198,8 +2373,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']:
         # 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(
             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
 
         # check failfirst
         ffirst_hit = False, False
@@ -2207,18 +2386,23 @@ 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
             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))
 
             ffirst_hit = (yield from self.check_ffirst(info, chk, srcstep))
 
+        # any modified return results?
+        yield from self.do_outregs(
+            info, outs, carry_en, ffirst_hit, ew_dst, outs_ok)
+
         # check if a FP Exception occurred. TODO for DD-FFirst, check VLi
         # and raise the exception *after* if VLi=1 but if VLi=0 then
         # check if a FP Exception occurred. TODO for DD-FFirst, check VLi
         # and raise the exception *after* if VLi=1 but if VLi=0 then
-        # truncate and make the exception "disappear". 
+        # truncate and make the exception "disappear".
         if self.FPSCR.FEX and (self.msr[MSRb.FE0] or self.msr[MSRb.FE1]):
             self.call_trap(0x700, PIb.FP)
             return
 
         if self.FPSCR.FEX and (self.msr[MSRb.FE0] or self.msr[MSRb.FE1]):
             self.call_trap(0x700, PIb.FP)
             return
 
-        # any modified return results?
-        yield from self.do_outregs_nia(asmop, ins_name, info, outs,
-                                       carry_en, rc_en, ffirst_hit, ew_dst)
+        yield from self.do_nia(asmop, ins_name, rc_en, ffirst_hit)
 
     def check_ffirst(self, info, rc_en, srcstep):
         """fail-first mode: checks a bit of Rc Vector, truncates VL
 
     def check_ffirst(self, info, rc_en, srcstep):
         """fail-first mode: checks a bit of Rc Vector, truncates VL
@@ -2255,7 +2439,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         yield Settle()  # let decoder update
         return True, vli_
 
         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"
         cr_out = yield self.dec2.op.cr_out
         if cr_out == CROutSel.CR1.value:
             rc_reg = "CR1"
@@ -2266,15 +2450,15 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         is_setvl = ins_name in ('svstep', 'setvl')
         if is_setvl:
             result = SelectableInt(result.vl, 64)
         is_setvl = ins_name in ('svstep', 'setvl')
         if is_setvl:
             result = SelectableInt(result.vl, 64)
-        #else:
+        # else:
         #    overflow = None  # do not override overflow except in setvl
 
         if rc_reg == "CR1":
             if cr1 is None:
         #    overflow = None  # do not override overflow except in setvl
 
         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)
                 log("default fp cr1", cr1)
             else:
                 log("explicit cr1", cr1)
@@ -2288,26 +2472,30 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             log("explicit rc0", cr0)
             self.crl[regnum].eq(cr0)
 
             log("explicit rc0", cr0)
             self.crl[regnum].eq(cr0)
 
-    def do_outregs_nia(self, asmop, ins_name, info, outs,
-                       ca_en, rc_en, ffirst_hit, ew_dst):
+    def do_outregs(self, info, outs, ca_en, ffirst_hit, ew_dst, outs_ok):
         ffirst_hit, vli = ffirst_hit
         # write out any regs for this instruction, but only if fail-first is ok
         # XXX TODO: allow CR-vector to be written out even if ffirst fails
         if not ffirst_hit or vli:
             for name, output in outs.items():
         ffirst_hit, vli = ffirst_hit
         # write out any regs for this instruction, but only if fail-first is ok
         # XXX TODO: allow CR-vector to be written out even if ffirst fails
         if not ffirst_hit or vli:
             for name, output in outs.items():
+                if not outs_ok[name]:
+                    log("skipping writing output with .ok=False", name, output)
+                    continue
                 yield from self.check_write(info, name, output, ca_en, ew_dst)
         # restore the CR value on non-VLI failfirst (from sv.cmp and others
         # which write directly to CR in the pseudocode (gah, what a mess)
         # if ffirst_hit and not vli:
         #    self.cr.value = self.cr_backup
 
                 yield from self.check_write(info, name, output, ca_en, ew_dst)
         # restore the CR value on non-VLI failfirst (from sv.cmp and others
         # which write directly to CR in the pseudocode (gah, what a mess)
         # if ffirst_hit and not vli:
         #    self.cr.value = self.cr_backup
 
+    def do_nia(self, asmop, ins_name, rc_en, ffirst_hit):
+        ffirst_hit, vli = ffirst_hit
         if ffirst_hit:
             self.svp64_reset_loop()
             nia_update = True
         else:
             # check advancement of src/dst/sub-steps and if PC needs updating
         if ffirst_hit:
             self.svp64_reset_loop()
             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()
 
         if nia_update:
             self.update_pc_next()
 
@@ -2362,7 +2550,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             else:
                 self.namespace['D'] = imm
 
             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)
         # 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)
@@ -2389,13 +2577,18 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         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:
         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))
+                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))
                 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:
             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))
                 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)
             reg_val = SelectableInt(0, ew_src)
         else:
             log('zero input reg %s %s' % (name, str(regnum)), is_vec)
             reg_val = SelectableInt(0, ew_src)
@@ -2487,7 +2680,8 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         # write special SPRs
         if name in info.special_regs:
             log('writing special %s' % name, output, special_sprs)
         # write special SPRs
         if name in info.special_regs:
             log('writing special %s' % name, output, special_sprs)
-            log("write reg %s 0x%x" % (name, output.value))
+            log("write reg %s 0x%x" % (name, output.value),
+                kind=LogType.InstrInOuts)
             if name in special_sprs:
                 self.spr[name] = output
             else:
             if name in special_sprs:
                 self.spr[name] = output
             else:
@@ -2514,7 +2708,7 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
             output = SelectableInt(0, EFFECTIVELY_UNLIMITED)
         log("write reg %s%s 0x%x ew %d" % (reg_prefix, str(regnum),
                                            output.value, ew_dst),
             output = SelectableInt(0, EFFECTIVELY_UNLIMITED)
         log("write reg %s%s 0x%x ew %d" % (reg_prefix, str(regnum),
                                            output.value, ew_dst),
-            kind=LogKind.InstrInOuts)
+            kind=LogType.InstrInOuts)
         # zero-extend tov64 bit begore storing (should use EXT oh well)
         if output.bits > 64:
             output = SelectableInt(output.value, 64)
         # zero-extend tov64 bit begore storing (should use EXT oh well)
         if output.bits > 64:
             output = SelectableInt(output.value, 64)
@@ -2683,6 +2877,9 @@ class ISACaller(ISACallerHelper, ISAFPHelpers, StepLoop):
         vfirst = self.svstate.vfirst
         log("    SV Vertical First", vf, vfirst)
         if not vf and vfirst == 1:
         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
 
             self.update_nia()
             return True
 
@@ -2749,7 +2946,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)
         # 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
         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