Maciej Fijalkowski avatar Maciej Fijalkowski committed 39ae410

start moving jit frames onto heap

Comments (0)

Files changed (11)

pypy/jit/backend/llsupport/gc.py

     @specialize.memo()
     def getframedescrs(self, cpu):
         descrs = JitFrameDescrs()
-        descrs.arraydescr = cpu.arraydescrof(jitframe.DEADFRAME)
-        descrs.as_int = cpu.interiorfielddescrof(jitframe.DEADFRAME,
-                                                 'int', 'jf_values')
-        descrs.as_ref = cpu.interiorfielddescrof(jitframe.DEADFRAME,
-                                                 'ref', 'jf_values')
-        descrs.as_float = cpu.interiorfielddescrof(jitframe.DEADFRAME,
-                                                   'float', 'jf_values')
-        descrs.jf_descr = cpu.fielddescrof(jitframe.DEADFRAME, 'jf_descr')
-        descrs.jf_guard_exc = cpu.fielddescrof(jitframe.DEADFRAME,
+        descrs.arraydescr = cpu.arraydescrof(jitframe.JITFRAME)
+        descrs.jf_descr = cpu.fielddescrof(jitframe.JITFRAME, 'jf_descr')
+        descrs.jf_guard_exc = cpu.fielddescrof(jitframe.JITFRAME,
                                                'jf_guard_exc')
+        descrs.jf_frame_info = cpu.fielddescrof(jitframe.JITFRAME,
+                                                'jf_frame_info')
+        descrs.jfi_frame_depth = cpu.fielddescrof(jitframe.JITFRAMEINFO,
+                                                  'jfi_frame_depth')
         return descrs
 
 class JitFrameDescrs:

pypy/jit/backend/llsupport/jitframe.py

-from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rpython.lltypesystem import lltype, llmemory
 
-GCINDEXLIST = lltype.GcArray(rffi.UINT)
+# this is an info that only depends on the assembler executed, copied from
+# compiled loop token (in fact we could use this as a compiled loop token
+# XXX do this
+
+JITFRAMEINFO = lltype.GcStruct(
+    'JITFRAMEINFO',
+    # the depth of frame
+    ('jfi_frame_depth', lltype.Signed),
+    # gcindexlist is a list of indexes of GC ptrs
+    # in the actual array jf_frame of JITFRAME
+    ('jfi_gcindexlist', lltype.Ptr(lltype.GcArray(lltype.Signed))),
+    )
 
 # the JITFRAME that's stored on the heap. See backend/<backend>/arch.py for
 # detailed explanation how it is on your architecture
 
 JITFRAME = lltype.GcStruct(
     'JITFRAME',
-    # gcindexlist is a list of indexes of GC ptrs
-    # in the actual array
-    ('jf_gcindexlist', lltype.Ptr(GCINDEXLIST)),
+    ('jf_frame_info', lltype.Ptr(JITFRAMEINFO)),
     # Once the execute_token() returns, the field 'jf_descr' stores the
     # descr of the last executed operation (either a GUARD, or FINISH).
     # This field is also set immediately before doing CALL_MAY_FORCE
     # For GUARD_(NO)_EXCEPTION and GUARD_NOT_FORCED: the exception we
     # got.  (Note that in case of a regular FINISH generated from
     # RPython code that finishes the function with an exception, the
-    # exception is not stored there, but in jf_values[0].ref.)
+    # exception is not stored there, but is simply kept as a variable there)
     ('jf_guard_exc', llmemory.GCREF),
     # the actual frame
     ('jf_frame', lltype.Array(lltype.Signed))

pypy/jit/backend/llsupport/llmodel.py

             return (rffi.cast(lltype.Signed, _exception_emulator) +
                     rffi.sizeof(lltype.Signed))
 
-        self._memoryerror_emulated = rffi.cast(llmemory.GCREF, -123)
-        self.deadframe_memoryerror = lltype.malloc(jitframe.DEADFRAME, 0)
-        self.deadframe_memoryerror.jf_guard_exc = self._memoryerror_emulated
+        # XXX I think we don't need it any more
+        #self._memoryerror_emulated = rffi.cast(llmemory.GCREF, -123)
+        #self.deadframe_memoryerror = lltype.malloc(jitframe.DEADFRAME, 0)
+        #self.deadframe_memoryerror.jf_guard_exc = self._memoryerror_emulated
 
         def propagate_exception():
             exc = _exception_emulator[1]
                 else:
                     assert deadframe.jf_descr == faildescr
             else:
+                XXX
                 deadframe = lltype.malloc(jitframe.DEADFRAME, 0)
                 deadframe.jf_guard_exc = rffi.cast(llmemory.GCREF, exc)
                 deadframe.jf_descr = faildescr
             slowpathaddr = rffi.cast(lltype.Signed, f)
             return endaddr, lengthaddr, slowpathaddr
 
-        self.deadframe_memoryerror = lltype.malloc(jitframe.DEADFRAME, 0)
+        #self.deadframe_memoryerror = lltype.malloc(jitframe.DEADFRAME, 0)
 
         def propagate_exception():
             addr = llop.get_exception_addr(llmemory.Address)
             faildescr = self.get_fail_descr_from_number(
                 self.propagate_exception_v)
             faildescr = faildescr.hide(self)
+            XXX
             deadframe = lltype.nullptr(jitframe.DEADFRAME)
             if exc:
                 try:
         return llhelper(self.PROPAGATE_EXCEPTION, self._propagate_exception)
 
     def grab_exc_value(self, deadframe):
+        XXX
         deadframe = lltype.cast_opaque_ptr(jitframe.DEADFRAMEPTR, deadframe)
         if not we_are_translated() and deadframe == self.deadframe_memoryerror:
             return "memoryerror!"       # for tests
         return deadframe.jf_guard_exc
 
     def set_savedata_ref(self, deadframe, data):
+        XXX
         deadframe = lltype.cast_opaque_ptr(jitframe.DEADFRAMEPTR, deadframe)
         deadframe.jf_savedata = data
 
     def get_savedata_ref(self, deadframe):
+        XXXX
         deadframe = lltype.cast_opaque_ptr(jitframe.DEADFRAMEPTR, deadframe)
         return deadframe.jf_savedata
 
                                                       abiname)
 
     def get_latest_descr(self, deadframe):
+        XXX
         deadframe = lltype.cast_opaque_ptr(jitframe.DEADFRAMEPTR, deadframe)
         descr = deadframe.jf_descr
         return history.AbstractDescr.show(self, descr)
 
     def get_latest_value_int(self, deadframe, index):
+        XXX
         deadframe = lltype.cast_opaque_ptr(jitframe.DEADFRAMEPTR, deadframe)
         return deadframe.jf_values[index].int
 
     def get_latest_value_ref(self, deadframe, index):
+        XXX
         deadframe = lltype.cast_opaque_ptr(jitframe.DEADFRAMEPTR, deadframe)
         return deadframe.jf_values[index].ref
 
     def get_latest_value_float(self, deadframe, index):
+        XXX
         deadframe = lltype.cast_opaque_ptr(jitframe.DEADFRAMEPTR, deadframe)
         return deadframe.jf_values[index].float
 
     def get_latest_value_count(self, deadframe):
+        XXX
         deadframe = lltype.cast_opaque_ptr(jitframe.DEADFRAMEPTR, deadframe)
         return len(deadframe.jf_values)
 

pypy/jit/backend/llsupport/regalloc.py

         self.used = []      # list of bools
         self.hint_frame_locations = {}
 
-    frame_depth = property(lambda:xxx, lambda:xxx)   # XXX kill me
-
     def get_frame_depth(self):
         return len(self.used)
 

pypy/jit/backend/llsupport/rewrite.py

                     continue
             # ----------
             self.newops.append(op)
-        # ---------- FINISH ----------
-        if len(self.newops) != 0 and self.newops[-1].getopnum() == rop.FINISH:
-            self.handle_finish(self.newops.pop())
-        # ----------
         return self.newops
 
     # ----------
             # assume that "self.gc_ll_descr.minimal_size_in_nursery" is 2 WORDs
             size = max(size, 2 * WORD)
             return (size + WORD-1) & ~(WORD-1)     # round up
-
-    # ----------
-
-    def handle_finish(self, finish_op):
-        v_deadframe = BoxPtr()
-        args_boxes = finish_op.getarglist()     # may contain Consts too
-        #
-        descrs = self.gc_ll_descr.getframedescrs(self.cpu)
-        #
-        op = ResOperation(rop.NEW_ARRAY,
-                          [ConstInt(len(args_boxes))], v_deadframe,
-                          descr=descrs.arraydescr)
-        self.handle_malloc_operation(op)
-        #
-        for i in range(len(args_boxes)):
-            # Generate setinteriorfields to write the args inside the
-            # deadframe object.  Ignore write barriers because it's a
-            # recent object.
-            box = args_boxes[i]
-            if box.type == history.INT: descr = descrs.as_int
-            elif box.type == history.REF: descr = descrs.as_ref
-            elif box.type == history.FLOAT: descr = descrs.as_float
-            else: assert 0, "bad box type?"
-            op = ResOperation(rop.SETINTERIORFIELD_GC,
-                              [v_deadframe, ConstInt(i), box], None,
-                              descr=descr)
-            self.newops.append(op)
-        #
-        # Generate a setfield to write the finish_op's descr.
-        gcref_descr = finish_op.getdescr().hide(self.cpu)
-        op = ResOperation(rop.SETFIELD_GC,
-                          [v_deadframe, ConstPtr(gcref_descr)], None,
-                          descr=descrs.jf_descr)
-        self.newops.append(op)
-        #
-        op = ResOperation(rop.FINISH, [v_deadframe], None)
-        self.newops.append(op)

pypy/jit/backend/x86/arch.py

 # one word for the force_index, and some extra space used only
 # during a malloc that needs to go via its slow path.
 
+# XXX FORCE_INDEX might need to go to GC frame
+
 if WORD == 4:
     # ebp + ebx + esi + edi + 4 extra words + force_index = 9 words
-    FRAME_FIXED_SIZE = 9
+    FRAME_FIXED_SIZE = 12 # 9 aligned to 16 bytes = 4 * WORD
     FORCE_INDEX_OFS = 0
     SAVED_REGISTERS = 1    # range(1, 5)
     MY_COPY_OF_REGS = 5    # range(5, 9)
 else:
     # rbp + rbx + r12 + r13 + r14 + r15 + 11 extra words + force_index = 18
-    FRAME_FIXED_SIZE = 18
+    FRAME_FIXED_SIZE = 18 # 18 aligned to 16 bytes = 2 * WORD
     FORCE_INDEX_OFS = 0
     SAVED_REGISTERS = 1    # range(1, 7)
     MY_COPY_OF_REGS = 7    # range(7, 18)

pypy/jit/backend/x86/assembler.py

 from pypy.jit.metainterp.history import Const, Box, BoxInt, ConstInt
 from pypy.jit.metainterp.history import AbstractFailDescr, INT, REF, FLOAT
 from pypy.jit.metainterp.history import JitCellToken
+from pypy.jit.backend.llsupport.descr import unpack_arraydescr
 from pypy.rpython.lltypesystem import lltype, rffi, rstr, llmemory
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rpython.annlowlevel import llhelper
         # for the duration of compiling one loop or a one bridge.
 
         clt = CompiledLoopToken(self.cpu, looptoken.number)
+        clt.frame_info = lltype.malloc(jitframe.JITFRAMEINFO)
         clt.allgcrefs = []
         looptoken.compiled_loop_token = clt
         if not we_are_translated():
         regalloc = RegAlloc(self, self.cpu.translate_support_code)
         #
         self._call_header_with_stack_check()
-        stackadjustpos = self._patchable_stackadjust()
         clt._debug_nbargs = len(inputargs)
         operations = regalloc.prepare_loop(inputargs, operations,
                                            looptoken, clt.allgcrefs)
         looppos = self.mc.get_relative_pos()
         looptoken._x86_loop_code = looppos
-        clt.frame_depth = -1     # temporarily
         frame_depth = self._assemble(regalloc, operations)
         clt.frame_depth = frame_depth
+        clt.frame_info.jfi_frame_depth = frame_depth
         #
         size_excluding_failure_stuff = self.mc.get_relative_pos()
         self.write_pending_failure_recoveries()
             rawstart + size_excluding_failure_stuff,
             rawstart))
         debug_stop("jit-backend-addr")
-        self._patch_stackadjust(rawstart + stackadjustpos, frame_depth)
         self.patch_pending_failure_recoveries(rawstart)
         #
         ops_offset = self.mc.ops_offset
                                              operations,
                                              self.current_clt.allgcrefs)
 
-        stackadjustpos = self._patchable_stackadjust()
         frame_depth = self._assemble(regalloc, operations)
         codeendpos = self.mc.get_relative_pos()
         self.write_pending_failure_recoveries()
         debug_print("bridge out of Guard %d has address %x to %x" %
                     (descr_number, rawstart, rawstart + codeendpos))
         debug_stop("jit-backend-addr")
-        self._patch_stackadjust(rawstart + stackadjustpos, frame_depth)
         self.patch_pending_failure_recoveries(rawstart)
         if not we_are_translated():
             # for the benefit of tests
         self.patch_jump_for_descr(faildescr, rawstart)
         ops_offset = self.mc.ops_offset
         self.fixup_target_tokens(rawstart)
-        self.current_clt.frame_depth = max(self.current_clt.frame_depth, frame_depth)
+        frame_depth = max(self.current_clt.frame_depth, frame_depth)
+        self.current_clt.frame_depth = frame_depth
+        self.current_clt.frame_info.jfi_frame_depth = frame_depth
         self.teardown()
         # oprofile support
         if self.cpu.profile_agent is not None:
             frame_depth = max(frame_depth, target_frame_depth)
         return frame_depth
 
-    def _patchable_stackadjust(self):
-        # stack adjustment LEA
-        self.mc.LEA32_rb(esp.value, 0)
-        return self.mc.get_relative_pos() - 4
-
-    def _patch_stackadjust(self, adr_lea, allocated_depth):
-        # patch stack adjustment LEA
-        mc = codebuf.MachineCodeBlockWrapper()
-        # Compute the correct offset for the instruction LEA ESP, [EBP-4*words]
-        mc.writeimm32(self._get_offset_of_ebp_from_esp(allocated_depth))
-        mc.copy_to_raw_memory(adr_lea)
-
-    def _get_offset_of_ebp_from_esp(self, allocated_depth):
-        # Given that [EBP] is where we saved EBP, i.e. in the last word
-        # of our fixed frame, then the 'words' value is:
-        words = (FRAME_FIXED_SIZE - 1) + allocated_depth
-        # align, e.g. for Mac OS X
-        aligned_words = align_stack_words(words+2)-2 # 2 = EIP+EBP
-        return -WORD * aligned_words
-
     def _call_header(self):
         # NB. the shape of the frame is hard-coded in get_basic_shape() too.
         # Also, make sure this is consistent with FRAME_FIXED_SIZE.
         self.mc.PUSH_r(ebp.value)
-        self.mc.MOV_rr(ebp.value, esp.value)
+        # XXX should be LEA?
+        self.mc.ADD_ri(esp.value, FRAME_FIXED_SIZE * WORD)
+        if IS_X86_64:
+            descrs = self.cpu.gc_ll_descr.getframedescrs(self.cpu)
+            _, ofs, _ = unpack_arraydescr(descrs.arraydescr)
+            self.mc.LEA_rm(ebp.value, (edi.value, ofs))
+        else:
+            xxx
+
         for loc in self.cpu.CALLEE_SAVE_REGISTERS:
             self.mc.PUSH_r(loc.value)
 
         self._call_header()
 
     def _call_footer(self):
-        self.mc.LEA_rb(esp.value, -len(self.cpu.CALLEE_SAVE_REGISTERS) * WORD)
+        self.mc.SUB_ri(esp.value, FRAME_FIXED_SIZE * WORD)
 
         gcrootmap = self.cpu.gc_ll_descr.gcrootmap
         if gcrootmap and gcrootmap.is_shadow_stack:
         # no malloc allowed here!!  xxx apart from one, hacking a lot
         #self.fail_ebp = allregisters[16 + ebp.value]
         num = 0
+        XXX
         deadframe = lltype.nullptr(jitframe.DEADFRAME)
         # step 1: lots of mess just to count the final value of 'num'
         bytecode1 = bytecode

pypy/jit/backend/x86/regalloc.py

         return self.min_frame_depth
 
     def _set_initial_bindings(self, inputargs):
-        if IS_X86_64:
-            inputargs = self._set_initial_bindings_regs_64(inputargs)
-        #                   ...
-        # stack layout:     arg2
-        #                   arg1
-        #                   arg0
-        #                   return address
-        #                   saved ebp        <-- ebp points here
-        #                   ...
-        cur_frame_pos = - 1 - FRAME_FIXED_SIZE
-        assert get_ebp_ofs(cur_frame_pos-1) == 2*WORD
-        assert get_ebp_ofs(cur_frame_pos-2) == 3*WORD
-        #
         for box in inputargs:
             assert isinstance(box, Box)
-            #
-            if IS_X86_32 and box.type == FLOAT:
-                cur_frame_pos -= 2
-            else:
-                cur_frame_pos -= 1
-            loc = self.fm.frame_pos(cur_frame_pos, box.type)
-            self.fm.set_binding(box, loc)
+            self.fm.get_new_loc(box)
+            #loc = self.fm.frame_pos(cur_frame_pos, box.type)
+            #self.fm.set_binding(box, loc)
 
-    def _set_initial_bindings_regs_64(self, inputargs):
-        # In reverse order for use with pop()
-        unused_gpr = [r9, r8, ecx, edx, esi, edi]
-        unused_xmm = [xmm7, xmm6, xmm5, xmm4, xmm3, xmm2, xmm1, xmm0]
-        #
-        pass_on_stack = []
-        #
-        for box in inputargs:
-            assert isinstance(box, Box)
-            #
-            if box.type == FLOAT:
-                if len(unused_xmm) > 0:
-                    ask = unused_xmm.pop()
-                    got = self.xrm.try_allocate_reg(box, selected_reg=ask)
-                    assert ask == got
-                else:
-                    pass_on_stack.append(box)
-            else:
-                if len(unused_gpr) > 0:
-                    ask = unused_gpr.pop()
-                    got = self.rm.try_allocate_reg(box, selected_reg=ask)
-                    assert ask == got
-                else:
-                    pass_on_stack.append(box)
-        #
-        return pass_on_stack
+        # if IS_X86_64:
+    #         inputargs = self._set_initial_bindings_regs_64(inputargs)
+    #     #                   ...
+    #     # stack layout:     arg2
+    #     #                   arg1
+    #     #                   arg0
+    #     #                   return address
+    #     #                   saved ebp        <-- ebp points here
+    #     #                   ...
+    #     XXX # adjust the address to count for the fact that we're passing
+    #     # jitframe as a first arg
+    #     cur_frame_pos = - 1 - FRAME_FIXED_SIZE
+    #     assert get_ebp_ofs(cur_frame_pos-1) == 2*WORD
+    #     assert get_ebp_ofs(cur_frame_pos-2) == 3*WORD
+    #     #
+    #     for box in inputargs:
+    #         assert isinstance(box, Box)
+    #         #
+    #         if IS_X86_32 and box.type == FLOAT:
+    #             cur_frame_pos -= 2
+    #         else:
+    #             cur_frame_pos -= 1
+    #         loc = self.fm.frame_pos(cur_frame_pos, box.type)
+    #         self.fm.set_binding(box, loc)
+
+    # def _set_initial_bindings_regs_64(self, inputargs):
+    #     # In reverse order for use with pop()
+    #     unused_gpr = [r9, r8, ecx, edx, esi] # jitframe comes in edi. don't use
+    #                                          # it for parameter parsing
+    #     unused_xmm = [xmm7, xmm6, xmm5, xmm4, xmm3, xmm2, xmm1, xmm0]
+    #     #
+    #     pass_on_stack = []
+    #     #
+    #     for box in inputargs:
+    #         assert isinstance(box, Box)
+    #         #
+    #         if box.type == FLOAT:
+    #             if len(unused_xmm) > 0:
+    #                 ask = unused_xmm.pop()
+    #                 got = self.xrm.try_allocate_reg(box, selected_reg=ask)
+    #                 assert ask == got
+    #             else:
+    #                 pass_on_stack.append(box)
+    #         else:
+    #             if len(unused_gpr) > 0:
+    #                 ask = unused_gpr.pop()
+    #                 got = self.rm.try_allocate_reg(box, selected_reg=ask)
+    #                 assert ask == got
+    #             else:
+    #                 pass_on_stack.append(box)
+    #     #
+    #     return pass_on_stack
 
     def possibly_free_var(self, var):
         if var.type == FLOAT:
     consider_guard_isnull = _consider_guard
 
     def consider_finish(self, op):
+        # the frame is in ebp, but we have to point where in the frame is
+        # the potential argument to FINISH
+        descr = op.getdescr()
+        self.force_spill_var(op.getarg(0))
         loc = self.loc(op.getarg(0))
-        self.Perform(op, [loc], None)
+        descr._x86_result_offset = loc.value
+        fail_no = self.assembler.cpu.get_fail_descr_number(descr)
+        self.Perform(op, [imm(fail_no)], None)
         self.possibly_free_var(op.getarg(0))
 
     def consider_guard_no_exception(self, op):
     # Argument is a frame position (0, 1, 2...).
     # Returns (ebp-20), (ebp-24), (ebp-28)...
     # i.e. the n'th word beyond the fixed frame size.
-    return -WORD * (FRAME_FIXED_SIZE + position)
+    return WORD * position
 
 def _valid_addressing_size(size):
     return size == 1 or size == 2 or size == 4 or size == 8

pypy/jit/backend/x86/regloc.py

         # _getregkey() returns self.value; the value returned must not
         # conflict with RegLoc._getregkey().  It doesn't a bit by chance,
         # so let it fail the following assert if it no longer does.
-        assert not (0 <= ebp_offset < 8 + 8 * IS_X86_64)
+        assert ebp_offset >= 0
+        #assert not (0 <= ebp_offset < 8 + 8 * IS_X86_64)
         self.position = position
         self.value = ebp_offset
         # One of INT, REF, FLOAT
         self.type = type
 
+    def _getregkey(self):
+        return -(self.value + 1)
+
     def get_width(self):
         if self.type == FLOAT:
             return 8

pypy/jit/backend/x86/runner.py

 from pypy.jit.backend.x86.arch import FORCE_INDEX_OFS, IS_X86_32
 from pypy.jit.backend.x86.profagent import ProfileAgent
 from pypy.jit.backend.llsupport.llmodel import AbstractLLCPU
+from pypy.jit.backend.llsupport import jitframe
 from pypy.jit.backend.x86 import regloc
 import sys
 
             setitem(index, null)
 
     def make_execute_token(self, *ARGS):
-        FUNCPTR = lltype.Ptr(lltype.FuncType(ARGS, llmemory.GCREF))
+        FUNCPTR = lltype.Ptr(lltype.FuncType([jitframe.JITFRAME],
+                                             lltype.Signed))
         #
         def execute_token(executable_token, *args):
             clt = executable_token.compiled_loop_token
             addr = executable_token._x86_function_addr
             func = rffi.cast(FUNCPTR, addr)
             #llop.debug_print(lltype.Void, ">>>> Entering", addr)
+            frame = lltype.malloc(jitframe.JITFRAME, clt.frame_depth)
+            frame.jf_frame_info = clt.frame_info
             prev_interpreter = None   # help flow space
             if not self.translate_support_code:
                 prev_interpreter = LLInterpreter.current_interpreter
                 LLInterpreter.current_interpreter = self.debug_ll_interpreter
             try:
-                deadframe = func(*args)
+                import pdb
+                pdb.set_trace()
+                descr_no = func(frame)
+                xxx
             finally:
                 if not self.translate_support_code:
                     LLInterpreter.current_interpreter = prev_interpreter
             #llop.debug_print(lltype.Void, "<<<< Back")
-            return deadframe
+            return frame
         return execute_token
 
     def cast_ptr_to_int(x):

pypy/jit/backend/x86/test/test_assembler.py

     registers[ACTUAL_CPU.NUM_REGS] = rffi.cast(rffi.LONG, descr_bytes)
     registers[ebp.value] = rffi.cast(rffi.LONG, stack) + WORD*stacklen
 
+    XXX # rewrite
+
     # run!
     assembler = Assembler386(FakeCPU())
     deadframe = assembler.failure_recovery_func(registers)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.