* [just for the record]: new prologue analyzer for S/390
@ 2003-04-18 21:53 Jim Blandy
2003-04-21 16:31 ` Andrew Cagney
2003-05-04 22:11 ` Jim Blandy
0 siblings, 2 replies; 6+ messages in thread
From: Jim Blandy @ 2003-04-18 21:53 UTC (permalink / raw)
To: gdb-patches
I think this patch shouldn't be committed; I'm just posting it for
reference.
This patch implements a new prologue analyzer for the S/390. It's
meant to be general enough to handle the complex prologues GCC emits
on the S/390, and robust enough to tolerate compiler changes. In my
experience, it does pretty well, even on optimized code.
However, the S/390 GDB folks at IBM and I agree that GDB on the S/390
should move towards using Dwarf 2 CFI and location lists whenever
possible, and do only minimal prologue analysis to handle those few
common cases where Dwarf 2 CFI is not available. And it looks to me
as if the work needed in GDB's core code to make it possible for any
target to use Dwarf 2 CFI is almost complete. In that light, it
doesn't make sense to dump a new, large, complex prologue analyzer
into the code base that will soon be eclipsed by a better solution.
So while this patch does improve the S/390 test results vs. the
current sources --- and a follow-up patch that depends on this one
improves things even more --- we don't think it should be committed.
This patch assumes that IBM's s390x ABI patch has been applied. That
patch is currently working its way through the IBM/FSF IP process.
http://sources.redhat.com/ml/gdb-patches/2003-02/msg00097.html
2003-04-18 Jim Blandy <jimb@redhat.com>
New S390 prologue analyzer.
* s390-tdep.c (struct prologue_value, enum pv_boolean): New types.
(pv_set_to_unknown, pv_set_to_constant, pv_set_to_register,
pv_constant_last, pv_add, pv_add_constant, pv_subtract,
pv_logical_and, pv_is_identical, pv_is_register, pv_is_array_ref,
compute_x_addr, s390_on_stack, s390_store,
s390_get_signal_frame_info): New functions.
(S390_NUM_SPILL_SLOTS): New macro.
(s390_get_frame_info): Rewritten.
(is_arg_reg): Deleted.
Break out the decoding of S/390 instructions into separate
functions, to make it more legible, and easier to check
against the spec.
* s390-tdep.c (is_ri, is_ril, is_rr, is_rre, is_rs, is_rse,
is_rx, is_rxe): New functions.
(op1_aghi, op2_aghi, op1_ahi, op2_ahi, op_ar, op_basr, op1_bras,
op2_bras, op_l, op_la, op1_larl, op2_larl, op_lgr, op1_lghi,
op2_lghi, op1_lhi, op2_lhi, op_lr, op_nr, op_ngr, op_s, op_st,
op_std, op1_stg, op2_stg, op_stm, op1_stmg, op2_stmg, op_svc): New
enums for opcode values. (Is this an improvement?)
* s390-tdep.c (s390_frame_align): New function.
(s390_gdbarch_init): Register it with the gdbarch object.
*** gdb/s390-tdep.c.~1~ Fri Apr 18 15:36:01 2003
--- gdb/s390-tdep.c Fri Apr 18 15:29:03 2003
***************
*** 189,679 ****
}
! /* Return true if REGIDX is the number of a register used to pass
! arguments, false otherwise. */
static int
! is_arg_reg (int regidx)
{
! return 2 <= regidx && regidx <= 6;
}
! /* s390_get_frame_info based on Hartmuts
! prologue definition in
! gcc-2.8.1/config/l390/linux.c
!
! It reads one instruction at a time & based on whether
! it looks like prologue code or not it makes a decision on
! whether the prologue is over, there are various state machines
! in the code to determine if the prologue code is possilby valid.
!
! This is done to hopefully allow the code survive minor revs of
! calling conventions.
!
! */
!
! int
! s390_get_frame_info (CORE_ADDR pc, struct frame_extra_info *fextra_info,
! struct frame_info *fi, int init_extra_info)
! {
! #define CONST_POOL_REGIDX 13
! #define GOT_REGIDX 12
! bfd_byte instr[S390_MAX_INSTR_SIZE];
! CORE_ADDR test_pc = pc, test_pc2;
! CORE_ADDR orig_sp = 0, save_reg_addr = 0, *saved_regs = NULL;
! int valid_prologue, good_prologue = 0;
! int gprs_saved[S390_NUM_GPRS];
! int fprs_saved[S390_NUM_FPRS];
! int regidx, instrlen;
! int const_pool_state;
! int varargs_state;
! int loop_cnt, gdb_gpr_store, gdb_fpr_store;
! int offset, expected_offset;
! int err = 0;
disassemble_info info;
! /* Have we seen an instruction initializing the frame pointer yet?
! If we've seen an `lr %r11, %r15', then frame_pointer_found is
! non-zero, and frame_pointer_regidx == 11. Otherwise,
! frame_pointer_found is zero and frame_pointer_regidx is 15,
! indicating that we're using the stack pointer as our frame
! pointer. */
! int frame_pointer_found = 0;
! int frame_pointer_regidx = 0xf;
!
! /* What we've seen so far regarding saving the back chain link:
! 0 -- nothing yet; sp still has the same value it had at the entry
! point. Since not all functions allocate frames, this is a
! valid state for the prologue to finish in.
! 1 -- We've saved the original sp in some register other than the
! frame pointer (hard-coded to be %r11, yuck).
! save_link_regidx is the register we saved it in.
! 2 -- We've seen the initial `bras' instruction of the sequence for
! reserving more than 32k of stack:
! bras %rX, .+8
! .long N
! s %r15, 0(%rX)
! where %rX is not the constant pool register.
! subtract_sp_regidx is %rX, and fextra_info->stack_bought is N.
! 3 -- We've reserved space for a new stack frame. This means we
! either saw a simple `ahi %r15,-N' in state 1, or the final
! `s %r15, ...' in state 2.
! 4 -- The frame and link are now fully initialized. We've
! reserved space for the new stack frame, and stored the old
! stack pointer captured in the back chain pointer field. */
! int save_link_state = 0;
! int save_link_regidx, subtract_sp_regidx;
!
! /* What we've seen so far regarding r12 --- the GOT (Global Offset
! Table) pointer. We expect to see `l %r12, N(%r13)', which loads
! r12 with the offset from the constant pool to the GOT, and then
! an `ar %r12, %r13', which adds the constant pool address,
! yielding the GOT's address. Here's what got_state means:
! 0 -- seen nothing
! 1 -- seen `l %r12, N(%r13)', but no `ar'
! 2 -- seen load and add, so GOT pointer is totally initialized
! When got_state is 1, then got_load_addr is the address of the
! load instruction, and got_load_len is the length of that
! instruction. */
! int got_state= 0;
! CORE_ADDR got_load_addr = 0, got_load_len = 0;
! const_pool_state = varargs_state = 0;
- memset (gprs_saved, 0, sizeof (gprs_saved));
- memset (fprs_saved, 0, sizeof (fprs_saved));
info.read_memory_func = dis_asm_read_memory;
! save_link_regidx = subtract_sp_regidx = 0;
! if (fextra_info)
{
! if (fi && get_frame_base (fi))
! {
! orig_sp = get_frame_base (fi);
! if (! init_extra_info && fextra_info->initialised)
! orig_sp += fextra_info->stack_bought;
! saved_regs = get_frame_saved_regs (fi);
! }
! if (init_extra_info || !fextra_info->initialised)
! {
! s390_memset_extra_info (fextra_info);
! fextra_info->function_start = pc;
! fextra_info->initialised = 1;
! }
! }
! instrlen = 0;
! do
! {
! valid_prologue = 0;
! test_pc += instrlen;
! /* add the previous instruction len */
! instrlen = s390_readinstruction (instr, test_pc, &info);
! if (instrlen < 0)
! {
! good_prologue = 0;
! err = -1;
! break;
! }
! /* We probably are in a glibc syscall */
! if (instr[0] == S390_SYSCALL_OPCODE && test_pc == pc)
! {
! good_prologue = 1;
! if (saved_regs && fextra_info && get_next_frame (fi)
! && get_frame_extra_info (get_next_frame (fi))
! && get_frame_extra_info (get_next_frame (fi))->sigcontext)
! {
! /* We are backtracing from a signal handler */
! save_reg_addr = get_frame_extra_info (get_next_frame (fi))->sigcontext +
! REGISTER_BYTE (S390_GP0_REGNUM);
! for (regidx = 0; regidx < S390_NUM_GPRS; regidx++)
! {
! saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
! save_reg_addr += S390_GPR_SIZE;
! }
! save_reg_addr = get_frame_extra_info (get_next_frame (fi))->sigcontext +
! (GDB_TARGET_IS_ESAME ? S390X_SIGREGS_FP0_OFFSET :
! S390_SIGREGS_FP0_OFFSET);
! for (regidx = 0; regidx < S390_NUM_FPRS; regidx++)
! {
! saved_regs[S390_FP0_REGNUM + regidx] = save_reg_addr;
! save_reg_addr += S390_FPR_SIZE;
! }
! }
! break;
! }
! if (save_link_state == 0)
! {
! /* check for a stack relative STMG or STM */
! if (((GDB_TARGET_IS_ESAME &&
! ((instr[0] == 0xeb) && (instr[5] == 0x24))) ||
! (instr[0] == 0x90)) && ((instr[2] >> 4) == 0xf))
! {
! regidx = (instr[1] >> 4);
! if (regidx < 6)
! varargs_state = 1;
! offset = ((instr[2] & 0xf) << 8) + instr[3];
! expected_offset =
! S390_GPR6_STACK_OFFSET + (S390_GPR_SIZE * (regidx - 6));
! if (offset != expected_offset)
! {
! good_prologue = 0;
! break;
! }
! if (saved_regs)
! save_reg_addr = orig_sp + offset;
! for (; regidx <= (instr[1] & 0xf); regidx++)
! {
! if (gprs_saved[regidx])
! {
! good_prologue = 0;
! break;
! }
! good_prologue = 1;
! gprs_saved[regidx] = 1;
! if (saved_regs)
! {
! saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
! save_reg_addr += S390_GPR_SIZE;
! }
! }
! valid_prologue = 1;
! continue;
! }
! }
! /* check for a stack relative STG or ST */
! if ((save_link_state == 0 || save_link_state == 3) &&
! ((GDB_TARGET_IS_ESAME &&
! ((instr[0] == 0xe3) && (instr[5] == 0x24))) ||
! (instr[0] == 0x50)) && ((instr[2] >> 4) == 0xf))
! {
! regidx = instr[1] >> 4;
! offset = ((instr[2] & 0xf) << 8) + instr[3];
! if (offset == 0)
! {
! if (save_link_state == 3 && regidx == save_link_regidx)
! {
! save_link_state = 4;
! valid_prologue = 1;
! continue;
! }
! else
! break;
! }
! if (regidx < 6)
! varargs_state = 1;
! expected_offset =
! S390_GPR6_STACK_OFFSET + (S390_GPR_SIZE * (regidx - 6));
! if (offset != expected_offset)
! {
! good_prologue = 0;
! break;
! }
! if (gprs_saved[regidx])
! {
! good_prologue = 0;
! break;
! }
! good_prologue = 1;
! gprs_saved[regidx] = 1;
! if (saved_regs)
! {
! save_reg_addr = orig_sp + offset;
! saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
! }
! valid_prologue = 1;
! continue;
! }
!
! /* Check for an fp-relative STG, ST, or STM. This is probably
! spilling an argument from a register out into a stack slot.
! This could be a user instruction, but if we haven't included
! any other suspicious instructions in the prologue, this
! could only be an initializing store, which isn't too bad to
! skip. The consequences of not including arg-to-stack spills
! are more serious, though --- you don't see the proper values
! of the arguments. */
! if ((save_link_state == 3 || save_link_state == 4)
! && ((instr[0] == 0x50 /* st %rA, D(%rX,%rB) */
! && (instr[1] & 0xf) == 0 /* %rX is zero, no index reg */
! && is_arg_reg ((instr[1] >> 4) & 0xf)
! && ((instr[2] >> 4) & 0xf) == frame_pointer_regidx)
! || (instr[0] == 0x90 /* stm %rA, %rB, D(%rC) */
! && is_arg_reg ((instr[1] >> 4) & 0xf)
! && is_arg_reg (instr[1] & 0xf)
! && ((instr[2] >> 4) & 0xf) == frame_pointer_regidx)))
! {
! valid_prologue = 1;
! continue;
! }
!
! /* check for STD */
! if (instr[0] == 0x60 && (instr[2] >> 4) == 0xf)
! {
! regidx = instr[1] >> 4;
! if (regidx == 0 || regidx == 2)
! varargs_state = 1;
! if (fprs_saved[regidx])
! {
! good_prologue = 0;
! break;
! }
! fprs_saved[regidx] = 1;
! if (saved_regs)
! {
! save_reg_addr = orig_sp + (((instr[2] & 0xf) << 8) + instr[3]);
! saved_regs[S390_FP0_REGNUM + regidx] = save_reg_addr;
! }
! valid_prologue = 1;
! continue;
! }
!
!
! if (const_pool_state == 0)
! {
!
! if (GDB_TARGET_IS_ESAME)
! {
! /* Check for larl CONST_POOL_REGIDX,offset on ESAME */
! if ((instr[0] == 0xc0)
! && (instr[1] == (CONST_POOL_REGIDX << 4)))
! {
! const_pool_state = 2;
! valid_prologue = 1;
! continue;
! }
! }
! else
! {
! /* Check for BASR gpr13,gpr0 used to load constant pool pointer to r13 in old compiler */
! if (instr[0] == 0xd && (instr[1] & 0xf) == 0
! && ((instr[1] >> 4) == CONST_POOL_REGIDX))
! {
! const_pool_state = 1;
! valid_prologue = 1;
! continue;
! }
! }
! /* Check for new fangled bras %r13,newpc to load new constant pool */
! /* embedded in code, older pre abi compilers also emitted this stuff. */
! if ((instr[0] == 0xa7) && ((instr[1] & 0xf) == 0x5) &&
! ((instr[1] >> 4) == CONST_POOL_REGIDX)
! && ((instr[2] & 0x80) == 0))
! {
! const_pool_state = 2;
! test_pc +=
! (((((instr[2] & 0xf) << 8) + instr[3]) << 1) - instrlen);
! valid_prologue = 1;
! continue;
! }
! }
! /* Check for AGHI or AHI CONST_POOL_REGIDX,val */
! if (const_pool_state == 1 && (instr[0] == 0xa7) &&
! ((GDB_TARGET_IS_ESAME &&
! (instr[1] == ((CONST_POOL_REGIDX << 4) | 0xb))) ||
! (instr[1] == ((CONST_POOL_REGIDX << 4) | 0xa))))
! {
! const_pool_state = 2;
! valid_prologue = 1;
! continue;
! }
! /* Check for LGR or LR gprx,15 */
! if ((GDB_TARGET_IS_ESAME &&
! instr[0] == 0xb9 && instr[1] == 0x04 && (instr[3] & 0xf) == 0xf) ||
! (instr[0] == 0x18 && (instr[1] & 0xf) == 0xf))
! {
! if (GDB_TARGET_IS_ESAME)
! regidx = instr[3] >> 4;
! else
! regidx = instr[1] >> 4;
! if (save_link_state == 0 && regidx != 0xb)
! {
! /* Almost defintely code for
! decrementing the stack pointer
! ( i.e. a non leaf function
! or else leaf with locals ) */
! save_link_regidx = regidx;
! save_link_state = 1;
! valid_prologue = 1;
! continue;
! }
! /* We use this frame pointer for alloca
! unfortunately we need to assume its gpr11
! otherwise we would need a smarter prologue
! walker. */
! if (!frame_pointer_found && regidx == 0xb)
! {
! frame_pointer_regidx = 0xb;
! frame_pointer_found = 1;
! if (fextra_info)
! fextra_info->frame_pointer_saved_pc = test_pc;
! valid_prologue = 1;
! continue;
! }
! }
! /* Check for AHI or AGHI gpr15,val */
! if (save_link_state == 1 && (instr[0] == 0xa7) &&
! ((GDB_TARGET_IS_ESAME && (instr[1] == 0xfb)) || (instr[1] == 0xfa)))
! {
! if (fextra_info)
! fextra_info->stack_bought =
! -extract_signed_integer (&instr[2], 2);
! save_link_state = 3;
! valid_prologue = 1;
! continue;
! }
! /* Alternatively check for the complex construction for
! buying more than 32k of stack
! BRAS gprx,.+8
! long val
! s %r15,0(%gprx) gprx currently r1 */
! if ((save_link_state == 1) && (instr[0] == 0xa7)
! && ((instr[1] & 0xf) == 0x5) && (instr[2] == 0)
! && (instr[3] == 0x4) && ((instr[1] >> 4) != CONST_POOL_REGIDX))
! {
! subtract_sp_regidx = instr[1] >> 4;
! save_link_state = 2;
! if (fextra_info)
! target_read_memory (test_pc + instrlen,
! (char *) &fextra_info->stack_bought,
! sizeof (fextra_info->stack_bought));
! test_pc += 4;
! valid_prologue = 1;
! continue;
! }
! if (save_link_state == 2 && instr[0] == 0x5b
! && instr[1] == 0xf0 &&
! instr[2] == (subtract_sp_regidx << 4) && instr[3] == 0)
! {
! save_link_state = 3;
! valid_prologue = 1;
! continue;
! }
! /* check for LA gprx,offset(15) used for varargs */
! if ((instr[0] == 0x41) && ((instr[2] >> 4) == 0xf) &&
! ((instr[1] & 0xf) == 0))
! {
! /* some code uses gpr7 to point to outgoing args */
! if (((instr[1] >> 4) == 7) && (save_link_state == 0) &&
! ((instr[2] & 0xf) == 0)
! && (instr[3] == S390_STACK_FRAME_OVERHEAD))
! {
! valid_prologue = 1;
! continue;
! }
! if (varargs_state == 1)
! {
! varargs_state = 2;
! valid_prologue = 1;
! continue;
! }
! }
! /* Check for a GOT load */
!
! if (GDB_TARGET_IS_ESAME)
! {
! /* Check for larl GOT_REGIDX, on ESAME */
! if ((got_state == 0) && (instr[0] == 0xc0)
! && (instr[1] == (GOT_REGIDX << 4)))
! {
! got_state = 2;
! valid_prologue = 1;
! continue;
! }
! }
else
! {
! /* check for l GOT_REGIDX,x(CONST_POOL_REGIDX) */
! if (got_state == 0 && const_pool_state == 2 && instr[0] == 0x58
! && (instr[2] == (CONST_POOL_REGIDX << 4))
! && ((instr[1] >> 4) == GOT_REGIDX))
! {
! got_state = 1;
! got_load_addr = test_pc;
! got_load_len = instrlen;
! valid_prologue = 1;
! continue;
! }
! /* Check for subsequent ar got_regidx,basr_regidx */
! if (got_state == 1 && instr[0] == 0x1a &&
! instr[1] == ((GOT_REGIDX << 4) | CONST_POOL_REGIDX))
! {
! got_state = 2;
! valid_prologue = 1;
! continue;
! }
! }
! }
! while (valid_prologue && good_prologue);
! if (good_prologue)
! {
! /* If this function doesn't reference the global offset table,
! then the compiler may use r12 for other things. If the last
! instruction we saw was a load of r12 from the constant pool,
! with no subsequent add to make the address PC-relative, then
! the load was probably a genuine body instruction; don't treat
! it as part of the prologue. */
! if (got_state == 1
! && got_load_addr + got_load_len == test_pc)
{
! test_pc = got_load_addr;
! instrlen = got_load_len;
}
! good_prologue = (((const_pool_state == 0) || (const_pool_state == 2)) &&
! ((save_link_state == 0) || (save_link_state == 4)) &&
! ((varargs_state == 0) || (varargs_state == 2)));
! }
! if (fextra_info)
! {
! fextra_info->good_prologue = good_prologue;
! fextra_info->skip_prologue_function_start =
! (good_prologue ? test_pc : pc);
! }
! if (saved_regs)
! /* The SP's element of the saved_regs array holds the old SP,
! not the address at which it is saved. */
! saved_regs[S390_SP_REGNUM] = orig_sp;
! return err;
}
--- 189,1441 ----
}
! /* Prologue analysis. */
!
! /* When we analyze a prologue, we're really doing 'abstract
! interpretation' or 'pseudo-evaluation': running the function's code
! in simulation, but using conservative approximations of the values
! it would have when it actually runs. For example, if our function
! starts with the instruction:
!
! ahi r1, 42 # add halfword immediate 42 to r1
!
! we don't know exactly what value will be in r1 after executing this
! instruction, but we do know it'll be 42 greater than its original
! value.
!
! If we then see an instruction like:
!
! ahi r1, 22 # add halfword immediate 22 to r1
!
! we still don't know what r1's value is, but again, we can say it is
! now 64 greater than its original value.
!
! If the next instruction were:
!
! lr r2, r1 # set r2 to r1's value
!
! then we can say that r2's value is now the original value of r1
! plus 64. And so on.
!
! Of course, this can only go so far before it gets unreasonable. If
! we wanted to be able to say anything about the value of r1 after
! the instruction:
!
! xr r1, r3 # exclusive-or r1 and r3, place result in r1
!
! then things would get pretty complex. But remember, we're just
! doing a conservative approximation; if exclusive-or instructions
! aren't relevant to prologues, we can just say r1's value is now
! 'unknown'. We can ignore things that are too complex, if that loss
! of information is acceptable for our application.
!
! Once you've reached an instruction that you don't know how to
! simulate, you stop. Now you examine the state of the registers and
! stack slots you've kept track of. For example:
!
! - To see how large your stack frame is, just check the value of sp;
! if it's the original value of sp minus a constant, then that
! constant is the stack frame's size. If the sp's value has been
! marked as 'unknown', then that means the prologue has done
! something too complex for us to track, and we don't know the
! frame size.
!
! - To see whether we've saved the SP in the current frame's back
! chain slot, we just check whether the current value of the back
! chain stack slot is the original value of the sp.
!
! Sure, this takes some work. But prologue analyzers aren't
! quick-and-simple pattern patching to recognize a few fixed prologue
! forms any more; they're big, hairy functions. Along with inferior
! function calls, prologue analysis accounts for a substantial
! portion of the time needed to stabilize a GDB port. So I think
! it's worthwhile to look for an approach that will be easier to
! understand and maintain. In the approach used here:
!
! - It's easier to see that the analyzer is correct: you just see
! whether the analyzer properly (albiet conservatively) simulates
! the effect of each instruction.
!
! - It's easier to extend the analyzer: you can add support for new
! instructions, and know that you haven't broken anything that
! wasn't already broken before.
!
! - It's orthogonal: to gather new information, you don't need to
! complicate the code for each instruction. As long as your domain
! of conservative values is already detailed enough to tell you
! what you need, then all the existing instruction simulations are
! already gathering the right data for you.
!
! A 'struct prologue_value' is a conservative approximation of the
! real value the register or stack slot will have. */
!
! struct prologue_value {
!
! /* What sort of value is this? This determines the interpretation
! of subsequent fields. */
! enum {
!
! /* We don't know anything about the value. This is also used for
! values we could have kept track of, when doing so would have
! been too complex and we don't want to bother. The bottom of
! our lattice. */
! pv_unknown,
!
! /* A known constant. K is its value. */
! pv_constant,
!
! /* The value that register REG originally had *UPON ENTRY TO THE
! FUNCTION*, plus K. If K is zero, this means, obviously, just
! the value REG had upon entry to the function. REG is a GDB
! register number. Before we start interpreting, we initialize
! every register R to { pv_register, R, 0 }. */
! pv_register,
!
! } kind;
!
! /* The meanings of the following fields depend on 'kind'; see the
! comments for the specific 'kind' values. */
! int reg;
! CORE_ADDR k;
! };
!
!
! /* Set V to be unknown. */
! static void
! pv_set_to_unknown (struct prologue_value *v)
! {
! v->kind = pv_unknown;
! }
!
!
! /* Set V to the constant K. */
! static void
! pv_set_to_constant (struct prologue_value *v, CORE_ADDR k)
! {
! v->kind = pv_constant;
! v->k = k;
! }
!
!
! /* Set V to the original value of register REG, plus K. */
! static void
! pv_set_to_register (struct prologue_value *v, int reg, CORE_ADDR k)
! {
! v->kind = pv_register;
! v->reg = reg;
! v->k = k;
! }
!
!
! /* If one of *A and *B is a constant, and the other isn't, swap the
! pointers as necessary to ensure that *B points to the constant.
! This can reduce the number of cases we need to analyze in the
! functions below. */
! static void
! pv_constant_last (struct prologue_value **a,
! struct prologue_value **b)
! {
! if ((*a)->kind == pv_constant
! && (*b)->kind != pv_constant)
! {
! struct prologue_value *temp = *a;
! *a = *b;
! *b = temp;
! }
! }
!
!
! /* Set SUM to the sum of A and B. SUM, A, and B may point to the same
! 'struct prologue_value' object. */
! static void
! pv_add (struct prologue_value *sum,
! struct prologue_value *a,
! struct prologue_value *b)
! {
! pv_constant_last (&a, &b);
!
! /* We can handle adding constants to registers, and other constants. */
! if (b->kind == pv_constant
! && (a->kind == pv_register
! || a->kind == pv_constant))
! {
! sum->kind = a->kind;
! sum->reg = a->reg; /* not meaningful if a is pv_constant, but
! harmless */
! sum->k = a->k + b->k;
! }
!
! /* Anything else we don't know how to add. We don't have a
! representation for, say, the sum of two registers, or a multiple
! of a register's value (adding a register to itself). */
! else
! sum->kind = pv_unknown;
! }
!
!
! /* Add the constant K to V. */
! static void
! pv_add_constant (struct prologue_value *v, CORE_ADDR k)
! {
! struct prologue_value pv_k;
!
! /* Rather than thinking of all the cases we can and can't handle,
! we'll just let pv_add take care of that for us. */
! pv_set_to_constant (&pv_k, k);
! pv_add (v, v, &pv_k);
! }
!
!
! /* Subtract B from A, and put the result in DIFF.
!
! This isn't quite the same as negating B and adding it to A, since
! we don't have a representation for the negation of anything but a
! constant. For example, we can't negate { pv_register, R1, 10 },
! but we do know that { pv_register, R1, 10 } minus { pv_register,
! R1, 5 } is { pv_constant, <ignored>, 5 }.
!
! This means, for example, that we can subtract two stack addresses;
! they're both relative to the original SP. Since the frame pointer
! is set based on the SP, its value will be the original SP plus some
! constant (probably zero), so we can use its value just fine. */
! static void
! pv_subtract (struct prologue_value *diff,
! struct prologue_value *a,
! struct prologue_value *b)
! {
! pv_constant_last (&a, &b);
!
! /* We can subtract a constant from another constant, or from a
! register. */
! if (b->kind == pv_constant
! && (a->kind == pv_register
! || a->kind == pv_constant))
! {
! diff->kind = a->kind;
! diff->reg = a->reg; /* not always meaningful, but harmless */
! diff->k = a->k - b->k;
! }
!
! /* We can subtract a register from itself, yielding a constant. */
! else if (a->kind == pv_register
! && b->kind == pv_register
! && a->reg == b->reg)
! {
! diff->kind = pv_constant;
! diff->k = a->k - b->k;
! }
!
! /* We don't know how to subtract anything else. */
! else
! diff->kind = pv_unknown;
! }
!
!
! /* Set AND to the logical and of A and B. */
! static void
! pv_logical_and (struct prologue_value *and,
! struct prologue_value *a,
! struct prologue_value *b)
! {
! pv_constant_last (&a, &b);
!
! /* We can 'and' two constants. */
! if (a->kind == pv_constant
! && b->kind == pv_constant)
! {
! and->kind = pv_constant;
! and->k = a->k & b->k;
! }
!
! /* We can 'and' anything with the constant zero. */
! else if (b->kind == pv_constant
! && b->k == 0)
! {
! and->kind = pv_constant;
! and->k = 0;
! }
!
! /* We can 'and' anything with ~0. */
! else if (b->kind == pv_constant
! && b->k == ~ (CORE_ADDR) 0)
! *and = *a;
!
! /* We can 'and' a register with itself. */
! else if (a->kind == pv_register
! && b->kind == pv_register
! && a->reg == b->reg
! && a->k == b->k)
! *and = *a;
!
! /* Otherwise, we don't know. */
! else
! pv_set_to_unknown (and);
! }
!
!
! /* Return non-zero iff A and B are identical expressions.
!
! This is not the same as asking if the two values are equal; the
! result of such a comparison would have to be a pv_boolean, and
! asking whether two 'unknown' values were equal would give you
! pv_maybe. Same for comparing, say, { pv_register, R1, 0 } and {
! pv_register, R2, 0}. Instead, this is asking whether the two
! representations are the same. */
! static int
! pv_is_identical (struct prologue_value *a,
! struct prologue_value *b)
! {
! if (a->kind != b->kind)
! return 0;
!
! switch (a->kind)
! {
! case pv_unknown:
! return 1;
! case pv_constant:
! return (a->k == b->k);
! case pv_register:
! return (a->reg == b->reg && a->k == b->k);
! default:
! gdb_assert (0);
! }
! }
!
!
! /* Return non-zero if A is the original value of register number R
! plus K, zero otherwise. */
! static int
! pv_is_register (struct prologue_value *a, int r, CORE_ADDR k)
! {
! return (a->kind == pv_register
! && a->reg == r
! && a->k == k);
! }
!
!
! /* A prologue-value-esque boolean type, including "maybe", when we
! can't figure out whether something is true or not. */
! enum pv_boolean {
! pv_maybe,
! pv_definite_yes,
! pv_definite_no,
! };
!
!
! /* Decide whether a reference to SIZE bytes at ADDR refers exactly to
! an element of an array. The array starts at ARRAY_ADDR, and has
! ARRAY_LEN values of ELT_SIZE bytes each. If ADDR definitely does
! refer to an array element, set *I to the index of the referenced
! element in the array, and return pv_definite_yes. If it definitely
! doesn't, return pv_definite_no. If we can't tell, return pv_maybe.
!
! If the reference does touch the array, but doesn't fall exactly on
! an element boundary, or doesn't refer to the whole element, return
! pv_maybe. */
! static enum pv_boolean
! pv_is_array_ref (struct prologue_value *addr,
! CORE_ADDR size,
! struct prologue_value *array_addr,
! CORE_ADDR array_len,
! CORE_ADDR elt_size,
! int *i)
! {
! struct prologue_value offset;
!
! /* Note that, since ->k is a CORE_ADDR, and CORE_ADDR is unsigned,
! if addr is *before* the start of the array, then this isn't going
! to be negative... */
! pv_subtract (&offset, addr, array_addr);
!
! if (offset.kind == pv_constant)
! {
! /* This is a rather odd test. We want to know if the SIZE bytes
! at ADDR don't overlap the array at all, so you'd expect it to
! be an || expression: "if we're completely before || we're
! completely after". But with unsigned arithmetic, things are
! different: since it's a number circle, not a number line, the
! right values for offset.k are actually one contiguous range. */
! if (offset.k <= -size
! && offset.k >= array_len * elt_size)
! return pv_definite_no;
! else if (offset.k % elt_size != 0
! || size != elt_size)
! return pv_maybe;
! else
! {
! *i = offset.k / elt_size;
! return pv_definite_yes;
! }
! }
! else
! return pv_maybe;
! }
!
!
!
! /* Decoding S/390 instructions. */
!
! /* Named opcode values for the S/390 instructions we recognize. Some
! instructions have their opcode split across two fields; those are the
! op1_* and op2_* enums. */
! enum
! {
! op1_aghi = 0xa7, op2_aghi = 0xb,
! op1_ahi = 0xa7, op2_ahi = 0xa,
! op_ar = 0x1a,
! op_basr = 0x0d,
! op1_bras = 0xa7, op2_bras = 0x5,
! op_l = 0x58,
! op_la = 0x41,
! op1_larl = 0xc0, op2_larl = 0x0,
! op_lgr = 0xb904,
! op1_lghi = 0xa7, op2_lghi = 0x9,
! op1_lhi = 0xa7, op2_lhi = 0x8,
! op_lr = 0x18,
! op_nr = 0x14,
! op_ngr = 0xb980,
! op_s = 0x5b,
! op_st = 0x50,
! op_std = 0x60,
! op1_stg = 0xe3, op2_stg = 0x24,
! op_stm = 0x90,
! op1_stmg = 0xeb, op2_stmg = 0x24,
! op_svc = 0x0a,
! };
!
!
! /* The functions below are for recognizing and decoding S/390
! instructions of various formats. Each of them checks whether INSN
! is an instruction of the given format, with the specified opcodes.
! If it is, it sets the remaining arguments to the values of the
! instruction's fields, and returns a non-zero value; otherwise, it
! returns zero.
!
! These functions' arguments appear in the order they appear in the
! instruction, not in the machine-language form. So, opcodes always
! come first, even though they're sometimes scattered around the
! instructions. And displacements appear before base and extension
! registers, as they do in the assembly syntax, not at the end, as
! they do in the machine language. */
! static int
! is_ri (bfd_byte *insn, int op1, int op2, unsigned int *r1, int *i2)
! {
! if (insn[0] == op1 && (insn[1] & 0xf) == op2)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! /* i2 is a 16-bit signed quantity. */
! *i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_ril (bfd_byte *insn, int op1, int op2,
! unsigned int *r1, int *i2)
! {
! if (insn[0] == op1 && (insn[1] & 0xf) == op2)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! /* i2 is a signed quantity. If the host 'int' is 32 bits long,
! no sign extension is necessary, but we don't want to assume
! that. */
! *i2 = (((insn[2] << 24)
! | (insn[3] << 16)
! | (insn[4] << 8)
! | (insn[5])) ^ 0x80000000) - 0x80000000;
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rr (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
! {
! if (insn[0] == op)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *r2 = insn[1] & 0xf;
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rre (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
! {
! if (((insn[0] << 8) | insn[1]) == op)
! {
! /* Yes, insn[3]. insn[2] is unused in RRE format. */
! *r1 = (insn[3] >> 4) & 0xf;
! *r2 = insn[3] & 0xf;
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rs (bfd_byte *insn, int op,
! unsigned int *r1, unsigned int *r3, unsigned int *d2, unsigned int *b2)
! {
! if (insn[0] == op)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *r3 = insn[1] & 0xf;
! *b2 = (insn[2] >> 4) & 0xf;
! *d2 = ((insn[2] & 0xf) << 8) | insn[3];
! return 1;
! }
! else
! return 0;
! }
!
!
static int
! is_rse (bfd_byte *insn, int op1, int op2,
! unsigned int *r1, unsigned int *r3, unsigned int *d2, unsigned int *b2)
! {
! if (insn[0] == op1
! /* Yes, insn[5]. insn[4] is unused. */
! && insn[5] == op2)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *r3 = insn[1] & 0xf;
! *b2 = (insn[2] >> 4) & 0xf;
! *d2 = (insn[2] & 0xf) | insn[3];
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rx (bfd_byte *insn, int op,
! unsigned int *r1, unsigned int *d2, unsigned int *x2, unsigned int *b2)
! {
! if (insn[0] == op)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *x2 = insn[1] & 0xf;
! *b2 = (insn[2] >> 4) & 0xf;
! *d2 = ((insn[2] & 0xf) << 8) | insn[3];
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rxe (bfd_byte *insn, int op1, int op2,
! unsigned int *r1, unsigned int *d2, unsigned int *x2, unsigned int *b2)
! {
! if (insn[0] == op1
! /* Yes, insn[5]. insn[4] is unused. */
! && insn[5] == op2)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *x2 = insn[1] & 0xf;
! *b2 = (insn[2] >> 4) & 0xf;
! *d2 = ((insn[2] & 0xf) << 8) | insn[3];
! return 1;
! }
! else
! return 0;
! }
!
!
! /* Set ADDR to the effective address for an X-style instruction, like:
!
! L R1, D2(X2, B2)
!
! Here, X2 and B2 are registers, and D2 is an unsigned 12-bit
! constant; the effective address is the sum of all three. If either
! X2 or B2 are zero, then it doesn't contribute to the sum --- this
! means that r0 can't be used as either X2 or B2.
!
! GPR is an array of general register values, indexed by GPR number,
! not GDB register number. */
! static void
! compute_x_addr (struct prologue_value *addr,
! struct prologue_value *gpr,
! unsigned int d2, unsigned int x2, unsigned int b2)
! {
! /* We can't just add stuff directly in addr; it might alias some of
! the registers we need to read. */
! struct prologue_value result;
!
! pv_set_to_constant (&result, d2);
! if (x2)
! pv_add (&result, &result, &gpr[x2]);
! if (b2)
! pv_add (&result, &result, &gpr[b2]);
!
! *addr = result;
! }
!
!
! /* The number of GPR and FPR spill slots in an S/390 stack frame. We
! track general-purpose registers r2 -- r15, and floating-point
! registers f0, f2, f4, and f6. */
! #define S390_NUM_SPILL_SLOTS (14 + 4)
!
!
! /* If the SIZE bytes at ADDR are a stack slot we're actually tracking,
! return pv_definite_yes and set *STACK to point to the slot. If
! we're sure that they are not any of our stack slots, then return
! pv_definite_no. Otherwise, return pv_maybe.
! - GPR is an array indexed by GPR number giving the current values
! of the general-purpose registers.
! - SPILL is an array tracking the spill area of the caller's frame;
! SPILL[i] is the i'th spill slot. The spill slots are designated
! for r2 -- r15, and then f0, f2, f4, and f6.
! - BACK_CHAIN is the value of the back chain slot; it's only valid
! when the current frame actually has some space for a back chain
! slot --- that is, when the current value of the stack pointer
! (according to GPR) is at least S390_STACK_FRAME_OVERHEAD bytes
! less than its original value. */
! static enum pv_boolean
! s390_on_stack (struct prologue_value *addr,
! CORE_ADDR size,
! struct prologue_value *gpr,
! struct prologue_value *spill,
! struct prologue_value *back_chain,
! struct prologue_value **stack)
! {
! struct prologue_value gpr_spill_addr;
! struct prologue_value fpr_spill_addr;
! struct prologue_value back_chain_addr;
! int i;
! enum pv_boolean b;
!
! /* Construct the addresses of the spill arrays and the back chain. */
! pv_set_to_register (&gpr_spill_addr, S390_SP_REGNUM, 2 * S390_GPR_SIZE);
! pv_set_to_register (&fpr_spill_addr, S390_SP_REGNUM, 16 * S390_GPR_SIZE);
! back_chain_addr = gpr[S390_SP_REGNUM - S390_GP0_REGNUM];
!
! /* We have to check for GPR and FPR references using two separate
! calls to pv_is_array_ref, since the GPR and FPR spill slots are
! different sizes. (SPILL is an array, but the thing it tracks
! isn't really an array.) */
!
! /* Was it a reference to the GPR spill array? */
! b = pv_is_array_ref (addr, size, &gpr_spill_addr, 14, S390_GPR_SIZE, &i);
! if (b == pv_definite_yes)
! {
! *stack = &spill[i];
! return pv_definite_yes;
! }
! if (b == pv_maybe)
! return pv_maybe;
!
! /* Was it a reference to the FPR spill array? */
! b = pv_is_array_ref (addr, size, &fpr_spill_addr, 4, S390_FPR_SIZE, &i);
! if (b == pv_definite_yes)
! {
! *stack = &spill[14 + i];
! return pv_definite_yes;
! }
! if (b == pv_maybe)
! return pv_maybe;
!
! /* Was it a reference to the back chain?
! This isn't quite right. We ought to check whether we have
! actually allocated any new frame at all. */
! b = pv_is_array_ref (addr, size, &back_chain_addr, 1, S390_GPR_SIZE, &i);
! if (b == pv_definite_yes)
! {
! *stack = back_chain;
! return pv_definite_yes;
! }
! if (b == pv_maybe)
! return pv_maybe;
!
! /* All the above queries returned definite 'no's. */
! return pv_definite_no;
! }
!
!
! /* Do a SIZE-byte store of VALUE to ADDR. GPR, SPILL, and BACK_CHAIN,
! and the return value are as described for s390_on_stack, above.
! Note that, when this returns pv_maybe, we have to assume that all
! of our memory now contains unknown values. */
! static enum pv_boolean
! s390_store (struct prologue_value *addr,
! CORE_ADDR size,
! struct prologue_value *value,
! struct prologue_value *gpr,
! struct prologue_value *spill,
! struct prologue_value *back_chain)
{
! struct prologue_value *stack;
! enum pv_boolean on_stack
! = s390_on_stack (addr, size, gpr, spill, back_chain, &stack);
!
! if (on_stack == pv_definite_yes)
! *stack = *value;
!
! return on_stack;
! }
!
!
! /* The current frame looks like a signal delivery frame: the first
! instruction is an 'svc' opcode. If the next frame is a signal
! handler's frame, set FI's saved register map to point into the
! signal context structure. */
! static void
! s390_get_signal_frame_info (struct frame_info *fi)
! {
! struct frame_info *next_frame = fi->next;
!
! if (next_frame
! && next_frame->extra_info
! && next_frame->extra_info->sigcontext)
! {
! /* We're definitely backtracing from a signal handler. */
! CORE_ADDR *saved_regs = fi->saved_regs;
! CORE_ADDR save_reg_addr = (next_frame->extra_info->sigcontext
! + REGISTER_BYTE (S390_GP0_REGNUM));
! int reg;
!
! for (reg = 0; reg < S390_NUM_GPRS; reg++)
! {
! saved_regs[S390_GP0_REGNUM + reg] = save_reg_addr;
! save_reg_addr += S390_GPR_SIZE;
! }
!
! save_reg_addr = (next_frame->extra_info->sigcontext
! + (GDB_TARGET_IS_ESAME ? S390X_SIGREGS_FP0_OFFSET :
! S390_SIGREGS_FP0_OFFSET));
! for (reg = 0; reg < S390_NUM_FPRS; reg++)
! {
! saved_regs[S390_FP0_REGNUM + reg] = save_reg_addr;
! save_reg_addr += S390_FPR_SIZE;
! }
! }
}
! static int
! s390_get_frame_info (CORE_ADDR start_pc,
! struct frame_extra_info *fextra_info,
! struct frame_info *fi,
! int init_extra_info)
! {
! /* Our return value:
! zero if we were able to read all the instructions we wanted, or
! -1 if we got an error trying to read memory. */
! int result = 0;
!
! /* We just use this for reading instructions. */
disassemble_info info;
! /* The current PC for our abstract interpretation. */
! CORE_ADDR pc;
! /* The address of the next instruction after that. */
! CORE_ADDR next_pc;
!
! /* The general-purpose registers. */
! struct prologue_value gpr[S390_NUM_GPRS];
!
! /* The floating-point registers. */
! struct prologue_value fpr[S390_NUM_FPRS];
!
! /* The register spill stack slots in the caller's frame ---
! general-purpose registers r2 through r15, and floating-point
! registers. spill[i] is where gpr i+2 gets spilled;
! spill[(14, 15, 16, 17)] is where (f0, f2, f4, f6) get spilled. */
! struct prologue_value spill[S390_NUM_SPILL_SLOTS];
!
! /* The value of the back chain slot. This is only valid if the stack
! pointer is known to be less than its original value --- that is,
! if we have indeed allocated space on the stack. */
! struct prologue_value back_chain;
!
! /* The address of the instruction after the last one that changed
! the SP, FP, or back chain. */
! CORE_ADDR after_last_frame_setup_insn = start_pc;
info.read_memory_func = dis_asm_read_memory;
! /* Set up everything's initial value. */
! {
! int i;
!
! for (i = 0; i < S390_NUM_GPRS; i++)
! pv_set_to_register (&gpr[i], S390_GP0_REGNUM + i, 0);
!
! for (i = 0; i < S390_NUM_FPRS; i++)
! pv_set_to_register (&fpr[i], S390_FP0_REGNUM + i, 0);
!
! for (i = 0; i < S390_NUM_SPILL_SLOTS; i++)
! pv_set_to_unknown (&spill[i]);
!
! pv_set_to_unknown (&back_chain);
! }
!
! /* Start interpreting instructions, until we hit something we don't
! know how to interpret. (Ideally, we should stop at the frame's
! real current PC, but at the moment, our callers don't give us
! that info.) */
! for (pc = start_pc; ; pc = next_pc)
{
! bfd_byte insn[S390_MAX_INSTR_SIZE];
! int insn_len = s390_readinstruction (insn, pc, &info);
!
! /* Fields for various kinds of instructions. */
! unsigned int b2, r1, r2, d2, x2, r3;
! int i2;
!
! /* The values of SP, FP, and back chain before this instruction,
! for detecting instructions that change them. */
! struct prologue_value pre_insn_sp, pre_insn_fp, pre_insn_back_chain;
!
! /* If we got an error trying to read the instruction, report it. */
! if (insn_len < 0)
! {
! result = -1;
! break;
! }
!
! next_pc = pc + insn_len;
!
! pre_insn_sp = gpr[S390_SP_REGNUM - S390_GP0_REGNUM];
! pre_insn_fp = gpr[S390_FRAME_REGNUM - S390_GP0_REGNUM];
! pre_insn_back_chain = back_chain;
!
! /* A special case, first --- only recognized as the very first
! instruction of the function, for signal delivery frames:
! SVC i --- system call */
! if (pc == start_pc
! && is_rr (insn, op_svc, &r1, &r2))
! {
! if (fi)
! s390_get_signal_frame_info (fi);
! break;
! }
!
! /* AHI r1, i2 --- add halfword immediate */
! else if (is_ri (insn, op1_ahi, op2_ahi, &r1, &i2))
! pv_add_constant (&gpr[r1], i2);
!
!
! /* AGHI r1, i2 --- add halfword immediate (64-bit version) */
! else if (GDB_TARGET_IS_ESAME
! && is_ri (insn, op1_aghi, op2_aghi, &r1, &i2))
! pv_add_constant (&gpr[r1], i2);
!
! /* AR r1, r2 -- add register */
! else if (is_rr (insn, op_ar, &r1, &r2))
! pv_add (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* BASR r1, 0 --- branch and save
! Since r2 is zero, this saves the PC in r1, but doesn't branch. */
! else if (is_rr (insn, op_basr, &r1, &r2)
! && r2 == 0)
! pv_set_to_constant (&gpr[r1], next_pc);
!
! /* BRAS r1, i2 --- branch relative and save */
! else if (is_ri (insn, op1_bras, op2_bras, &r1, &i2))
! {
! pv_set_to_constant (&gpr[r1], next_pc);
! next_pc = pc + i2 * 2;
!
! /* We'd better not interpret any backward branches. We'll
! never terminate. */
! if (next_pc <= pc)
! break;
! }
!
! /* L r1, d2(x2, b2) --- load */
! else if (is_rx (insn, op_l, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
! struct prologue_value *stack;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! /* If it's a load from an in-line constant pool, then we can
! simulate that, under the assumption that the code isn't
! going to change between the time the processor actually
! executed it creating the current frame, and the time when
! we're analyzing the code to unwind past that frame. */
! if (addr.kind == pv_constant
! && start_pc <= addr.k
! && addr.k < next_pc)
! pv_set_to_constant (&gpr[r1],
! read_memory_integer (addr.k, 4));
!
! /* If it's definitely a reference to something on the stack,
! we can do that. */
! else if (s390_on_stack (&addr, 4, gpr, spill, &back_chain, &stack)
! == pv_definite_yes)
! gpr[r1] = *stack;
!
! /* Otherwise, we don't know the value. */
! else
! pv_set_to_unknown (&gpr[r1]);
! }
!
! /* LA r1, d2(x2, b2) --- load address */
! else if (is_rx (insn, op_la, &r1, &d2, &x2, &b2))
! compute_x_addr (&gpr[r1], gpr, d2, x2, b2);
!
! /* LARL r1, i2 --- load address relative long */
! else if (GDB_TARGET_IS_ESAME
! && is_ril (insn, op1_larl, op2_larl, &r1, &i2))
! pv_set_to_constant (&gpr[r1], pc + i2 * 2);
!
! /* LGR r1, r2 --- load from register */
! else if (GDB_TARGET_IS_ESAME
! && is_rre (insn, op_lgr, &r1, &r2))
! gpr[r1] = gpr[r2];
!
! /* LHI r1, i2 --- load halfword immediate */
! else if (is_ri (insn, op1_lhi, op2_lhi, &r1, &i2))
! pv_set_to_constant (&gpr[r1], i2);
!
! /* LGHI r1, i2 --- load halfword immediate --- 64-bit version */
! else if (is_ri (insn, op1_lghi, op2_lghi, &r1, &i2))
! pv_set_to_constant (&gpr[r1], i2);
!
! /* LR r1, r2 --- load from register */
! else if (is_rr (insn, op_lr, &r1, &r2))
! gpr[r1] = gpr[r2];
!
! /* NGR r1, r2 --- logical and --- 64-bit version */
! else if (GDB_TARGET_IS_ESAME
! && is_rre (insn, op_ngr, &r1, &r2))
! pv_logical_and (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* NR r1, r2 --- logical and */
! else if (is_rr (insn, op_nr, &r1, &r2))
! pv_logical_and (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* NGR r1, r2 --- logical and --- 64-bit version */
! else if (GDB_TARGET_IS_ESAME
! && is_rre (insn, op_ngr, &r1, &r2))
! pv_logical_and (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* NR r1, r2 --- logical and */
! else if (is_rr (insn, op_nr, &r1, &r2))
! pv_logical_and (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* S r1, d2(x2, b2) --- subtract from memory */
! else if (is_rx (insn, op_s, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
! struct prologue_value value;
! struct prologue_value *stack;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! /* If it's a load from an in-line constant pool, then we can
! simulate that, under the assumption that the code isn't
! going to change between the time the processor actually
! executed it and the time when we're analyzing it. */
! if (addr.kind == pv_constant
! && start_pc <= addr.k
! && addr.k < pc)
! pv_set_to_constant (&value, read_memory_integer (addr.k, 4));
!
! /* If it's definitely a reference to something on the stack,
! we could do that. */
! else if (s390_on_stack (&addr, 4, gpr, spill, &back_chain, &stack)
! == pv_definite_yes)
! value = *stack;
!
! /* Otherwise, we don't know the value. */
! else
! pv_set_to_unknown (&value);
!
! pv_subtract (&gpr[r1], &gpr[r1], &value);
! }
!
! /* ST r1, d2(x2, b2) --- store */
! else if (is_rx (insn, op_st, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! /* The below really should be '4', not 'S390_GPR_SIZE'; this
! instruction always stores 32 bits, regardless of the full
! size of the GPR. */
! if (s390_store (&addr, 4, &gpr[r1], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* STD r1, d2(x2,b2) --- store floating-point register */
! else if (is_rx (insn, op_std, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! if (s390_store (&addr, 8, &fpr[r1], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* STG r1, d2(x2, b2) --- 64-bit store */
! else if (GDB_TARGET_IS_ESAME
! && is_rxe (insn, op1_stg, op2_stg, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! /* The below really should be '8', not 'S390_GPR_SIZE'; this
! instruction always stores 64 bits, regardless of the full
! size of the GPR. */
! if (s390_store (&addr, 8, &gpr[r1], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* STM r1, r3, d2(b2) --- store multiple */
! else if (is_rs (insn, op_stm, &r1, &r3, &d2, &b2))
! {
! int regnum;
! int offset;
! struct prologue_value addr;
!
! for (regnum = r1, offset = 0;
! regnum <= r3;
! regnum++, offset += 4)
! {
! compute_x_addr (&addr, gpr, d2 + offset, 0, b2);
!
! if (s390_store (&addr, 4, &gpr[regnum], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* If we left the loop early, we should stop interpreting
! altogether. */
! if (regnum <= r3)
! break;
! }
!
! /* STMG r1, r3, d2(b2) --- store multiple, 64-bit */
! else if (is_rse (insn, op1_stmg, op2_stmg, &r1, &r3, &d2, &b2))
! {
! int regnum;
! int offset;
! struct prologue_value addr;
!
! for (regnum = r1, offset = 0;
! regnum <= r3;
! regnum++, offset += 8)
! {
! compute_x_addr (&addr, gpr, d2 + offset, 0, b2);
!
! if (s390_store (&addr, 8, &gpr[regnum], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* If we left the loop early, we should stop interpreting
! altogether. */
! if (regnum <= r3)
! break;
! }
!
else
! /* An instruction we don't know how to simulate. The only
! safe thing to do would be to set every value we're tracking
! to 'unknown'. Instead, we'll be optimistic: we just stop
! interpreting, and assume that the machine state we've got
! now is good enough for unwinding the stack. */
! break;
!
! /* Record the address after the last instruction that changed
! the FP, SP, or backlink. Ignore instructions that changed
! them back to their original values --- those are probably
! restore instructions. (The back chain is never restored,
! just popped.) */
! {
! struct prologue_value *sp = &gpr[S390_SP_REGNUM - S390_GP0_REGNUM];
! struct prologue_value *fp = &gpr[S390_FRAME_REGNUM - S390_GP0_REGNUM];
!
! if ((! pv_is_identical (&pre_insn_sp, sp)
! && ! pv_is_register (sp, S390_SP_REGNUM, 0))
! || (! pv_is_identical (&pre_insn_fp, fp)
! && ! pv_is_register (fp, S390_FRAME_REGNUM, 0))
! || ! pv_is_identical (&pre_insn_back_chain, &back_chain))
! after_last_frame_setup_insn = next_pc;
! }
! }
!
! /* Okay, now gpr[], fpr[], spill[], and back_chain reflect the state
! of the machine as of the first instruction we couldn't interpret
! (hopefully the first non-prologue instruction). */
! {
! /* The size of the frame, or (CORE_ADDR) -1 if we couldn't figure
! that out. */
! CORE_ADDR frame_size = -1;
!
! /* The value the SP had upon entry to the function, or
! (CORE_ADDR) -1 if we can't figure that out. */
! CORE_ADDR original_sp = -1;
!
! /* If the analysis said that the current SP value is the original
! value less some constant, then that constant is the frame size. */
! {
! struct prologue_value *sp = &gpr[S390_SP_REGNUM - S390_GP0_REGNUM];
!
! if (sp->kind == pv_register
! && sp->reg == S390_SP_REGNUM)
! frame_size = -sp->k;
! }
!
! /* We may be able to use the current SP to compute the old SP. */
! if (frame_size != -1 && fi && get_frame_base (fi))
! original_sp = get_frame_base (fi) + frame_size;
!
! /* If we knew other registers' current values, we could check if
! the analysis said any of those were related to the original SP
! value, too. But for now, we'll just punt. */
!
! /* If the caller passed in an 'extra info' structure, fill in the
! parts we can. */
! if (fextra_info)
! {
! if (init_extra_info || ! fextra_info->initialised)
! {
! s390_memset_extra_info (fextra_info);
! fextra_info->function_start = start_pc;
! fextra_info->initialised = 1;
! }
!
! if (frame_size != -1)
! {
! fextra_info->stack_bought = frame_size;
! }
!
! /* Assume everything was okay, and indicate otherwise when we
! find something amiss. */
! fextra_info->good_prologue = 1;
!
! /* Did we ever copy the original SP to the frame pointer
! register? If so, then that suggests the function is going
! to use alloca. */
{
! struct prologue_value *fp
! = &gpr[S390_FRAME_REGNUM - S390_GP0_REGNUM];
!
! /* Is the FP some offset from the original SP value? */
! if (fp->kind == pv_register
! && fp->reg == S390_SP_REGNUM)
! /* Actually, nobody cares about the exact PC, so any
! non-zero value will do here. */
! fextra_info->frame_pointer_saved_pc = 1;
!
! /* If we didn't save the original SP in the FP, and we don't
! know the frame size, then we're not going to be able to
! find the calling frame. */
! else if (frame_size == -1)
! fextra_info->good_prologue = 0;
}
! if (fextra_info->good_prologue)
! fextra_info->skip_prologue_function_start
! = after_last_frame_setup_insn;
! else
! /* If the prologue was too complex for us to make sense of,
! then perhaps it's better to just not skip anything at
! all. */
! fextra_info->skip_prologue_function_start = start_pc;
! }
!
! /* Indicate where registers were saved on the stack, if:
! - the caller seems to want to know,
! - the caller provided an actual SP, and
! - the analysis gave us enough information to actually figure it
! out. */
! if (fi
! && fi->saved_regs
! && original_sp != -1)
! {
! int slot_num;
! CORE_ADDR slot_addr;
!
! /* Scan the spill array; if a spill slot says it holds the
! original value of some register, then record that slot's
! address as the place that register was saved.
!
! Just for kicks, note that, even if registers aren't saved
! in their officially-sanctioned slots, this will still work
! --- we know what really got put where. */
!
! /* First, the slots for r2 -- r15. */
! for (slot_num = 0, slot_addr = original_sp + 2 * S390_GPR_SIZE;
! slot_num < 14;
! slot_num++, slot_addr += S390_GPR_SIZE)
! {
! struct prologue_value *slot = &spill[slot_num];
!
! if (slot->kind == pv_register
! && slot->k == 0)
! fi->saved_regs[slot->reg] = slot_addr;
! }
!
! /* Then, the slots for f0, f2, f4, and f6. They're a
! different size. */
! for (slot_num = 14, slot_addr = original_sp + 16 * S390_GPR_SIZE;
! slot_num < S390_NUM_SPILL_SLOTS;
! slot_num++, slot_addr += S390_FPR_SIZE)
! {
! struct prologue_value *slot = &spill[slot_num];
!
! if (slot->kind == pv_register
! && slot->k == 0)
! fi->saved_regs[slot->reg] = slot_addr;
! }
!
! /* The stack pointer's element of saved_regs[] is special. */
! fi->saved_regs[S390_SP_REGNUM] = original_sp;
! }
! }
!
! return result;
}
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [just for the record]: new prologue analyzer for S/390
2003-04-18 21:53 [just for the record]: new prologue analyzer for S/390 Jim Blandy
@ 2003-04-21 16:31 ` Andrew Cagney
2003-04-21 16:38 ` Daniel Jacobowitz
2003-04-21 21:54 ` Jim Blandy
2003-05-04 22:11 ` Jim Blandy
1 sibling, 2 replies; 6+ messages in thread
From: Andrew Cagney @ 2003-04-21 16:31 UTC (permalink / raw)
To: Jim Blandy; +Cc: gdb-patches
> I think this patch shouldn't be committed; I'm just posting it for
> reference.
>
> This patch implements a new prologue analyzer for the S/390. It's
> meant to be general enough to handle the complex prologues GCC emits
> on the S/390, and robust enough to tolerate compiler changes. In my
> experience, it does pretty well, even on optimized code.
>
> However, the S/390 GDB folks at IBM and I agree that GDB on the S/390
> should move towards using Dwarf 2 CFI and location lists whenever
> possible, and do only minimal prologue analysis to handle those few
> common cases where Dwarf 2 CFI is not available. And it looks to me
> as if the work needed in GDB's core code to make it possible for any
> target to use Dwarf 2 CFI is almost complete. In that light, it
> doesn't make sense to dump a new, large, complex prologue analyzer
> into the code base that will soon be eclipsed by a better solution.
It is important to ensure that all technical decisions related to GDB
are discussed here, in this forum. Issues, specific to this change,
that come to mind, include:
- the unpredictability of the CFI timetable (a bird in the hand vs ...)
- the unpredictability of the legal processes sometimes required before
accepting changes (must assume that the change doesn't exist)
- the need to get the existing s390's frame code updated so that it
implements frame-unwind (this is in addition, and largely orthogonal to
the missing generic CFI unwinder). Ignoring s390_get_signal_frame_info,
which should be separated out, the attached appears to only affect the
prologe analyzer so is relatively independant?
- the need to have gdb behave reasonably well when there is no, or
minimal, debug info
- the need to fix the s390 problem of having a wrong frame ID .
stack_addr (does this change address that problem?)
- what tests it actually fixes (data?)
> This patch assumes that IBM's s390x ABI patch has been applied. That
> patch is currently working its way through the IBM/FSF IP process.
> http://sources.redhat.com/ml/gdb-patches/2003-02/msg00097.html
To be honest, I don't understand the dependency. This patch modifies
the prologue analysier while the other change appears to be fixing the
push_arguments function. While they might interact in positive ways, I
don't think that they are dependant.
I'd also strongly recommend putting your the file / changes through
gdb_indent.sh - GNU indent will likely have a field day on the
formatting of the comments :-(
Andrew
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [just for the record]: new prologue analyzer for S/390
2003-04-21 16:31 ` Andrew Cagney
@ 2003-04-21 16:38 ` Daniel Jacobowitz
2003-04-21 21:11 ` Andrew Cagney
2003-04-21 21:54 ` Jim Blandy
1 sibling, 1 reply; 6+ messages in thread
From: Daniel Jacobowitz @ 2003-04-21 16:38 UTC (permalink / raw)
To: Andrew Cagney; +Cc: Jim Blandy, gdb-patches
On Mon, Apr 21, 2003 at 12:31:34PM -0400, Andrew Cagney wrote:
> >I think this patch shouldn't be committed; I'm just posting it for
> >reference.
> >
> >This patch implements a new prologue analyzer for the S/390. It's
> >meant to be general enough to handle the complex prologues GCC emits
> >on the S/390, and robust enough to tolerate compiler changes. In my
> >experience, it does pretty well, even on optimized code.
> >
> >However, the S/390 GDB folks at IBM and I agree that GDB on the S/390
> >should move towards using Dwarf 2 CFI and location lists whenever
> >possible, and do only minimal prologue analysis to handle those few
> >common cases where Dwarf 2 CFI is not available. And it looks to me
> >as if the work needed in GDB's core code to make it possible for any
> >target to use Dwarf 2 CFI is almost complete. In that light, it
> >doesn't make sense to dump a new, large, complex prologue analyzer
> >into the code base that will soon be eclipsed by a better solution.
> - the need to have gdb behave reasonably well when there is no, or
> minimal, debug info
The others aside for the moment, I believe this is the best reason to
continue to support the prologue analyzers.
On the other hand, we're making active progress in providing more and
better CFI; and now we support debug info in a separate file from the
code; so some day soon I expect that I won't even care how GDB behaves
without debug info except for debugging other people's proprietary
software to which I don't have source. And in that case (which comes
up occasionally) the level of sophistication needed from a prologue
analyzer is extremely low.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [just for the record]: new prologue analyzer for S/390
2003-04-21 16:38 ` Daniel Jacobowitz
@ 2003-04-21 21:11 ` Andrew Cagney
0 siblings, 0 replies; 6+ messages in thread
From: Andrew Cagney @ 2003-04-21 21:11 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Jim Blandy, gdb-patches
> The others aside for the moment, I believe this is the best reason to
> continue to support the prologue analyzers.
>
> On the other hand, we're making active progress in providing more and
> better CFI; and now we support debug info in a separate file from the
> code;
GDB might support it, binutils doesn't generate it :-(
Andrew
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [just for the record]: new prologue analyzer for S/390
2003-04-21 16:31 ` Andrew Cagney
2003-04-21 16:38 ` Daniel Jacobowitz
@ 2003-04-21 21:54 ` Jim Blandy
1 sibling, 0 replies; 6+ messages in thread
From: Jim Blandy @ 2003-04-21 21:54 UTC (permalink / raw)
To: Andrew Cagney; +Cc: gdb-patches
Andrew Cagney <ac131313@redhat.com> writes:
> It is important to ensure that all technical decisions related to GDB
> are discussed here, in this forum. Issues, specific to this change,
> that come to mind, include:
I think it's okay to chat with folks off-line to get their thoughts on
a topic, as long as the decisions are made here. I haven't sworn on a
bible not to commit the patch, just adjusted my presentation of it so
as not to shock the people who, if legal strangeness weren't in the
way, would be maintaining the code in question.
> - the unpredictability of the CFI timetable (a bird in the hand vs
> ...)
Right; I think that's the only point in favor of the patch: it does
work, and is committable right now.
> - the unpredictability of the legal processes sometimes required
> before accepting changes (must assume that the change doesn't exist)
Well, the present situation doesn't depend on any changes from IBM:
the choice is between arch-generic Dwarf 2 CFI support or mondo
prologue analysis.
> - the need to get the existing s390's frame code updated so that it
> implements frame-unwind (this is in addition, and largely orthogonal
> to the missing generic CFI unwinder). Ignoring
> s390_get_signal_frame_info, which should be separated out, the
> attached appears to only affect the prologe analyzer so is relatively
> independant?
Yes, it's a drop-in replacement.
Even s390_get_signal_frame_info consists of code which used to be part
of the old prologue analyzer; breaking it out is a separate patch. It
looks to me, though, that if one simply changed s390-tdep.c to use the
modern frame unwinding stuff, it would go away, or at least work very
differently, anyway.
> - the need to have gdb behave reasonably well when there is no, or
> minimal, debug info
>
> - the need to fix the s390 problem of having a wrong frame ID
> . stack_addr (does this change address that problem?)
Nope. Its only effect is to make prologue analysis accurate more
often.
I've put together a list of projectlets needed to bring the s390 up to
date, which I'll post in a bit; fixing the frame ID is on that list.
> - what tests it actually fixes (data?)
I'll go make that list. It doesn't seem to have a major effect on the
test suite, but it makes a big difference when debugging optimized
code.
> > This patch assumes that IBM's s390x ABI patch has been applied. That
> > patch is currently working its way through the IBM/FSF IP process.
> > http://sources.redhat.com/ml/gdb-patches/2003-02/msg00097.html
>
> To be honest, I don't understand the dependency. This patch modifies
> the prologue analysier while the other change appears to be fixing the
> push_arguments function. While they might interact in positive ways,
> I don't think that they are dependant.
The patch doesn't apply. The context doesn't match, textually. There
is no major semantic dependency.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [just for the record]: new prologue analyzer for S/390
2003-04-18 21:53 [just for the record]: new prologue analyzer for S/390 Jim Blandy
2003-04-21 16:31 ` Andrew Cagney
@ 2003-05-04 22:11 ` Jim Blandy
1 sibling, 0 replies; 6+ messages in thread
From: Jim Blandy @ 2003-05-04 22:11 UTC (permalink / raw)
To: gdb-patches
Here's a revision of this patch, with some improved behavior on the
64-bit s390x variant of the architecture.
2003-04-18 Jim Blandy <jimb@redhat.com>
New S390 prologue analyzer.
* s390-tdep.c (struct prologue_value, enum pv_boolean): New types.
(pv_set_to_unknown, pv_set_to_constant, pv_set_to_register,
pv_constant_last, pv_add, pv_add_constant, pv_subtract,
pv_logical_and, pv_is_identical, pv_is_register, pv_is_array_ref,
compute_x_addr, s390_on_stack, s390_store,
s390_get_signal_frame_info): New functions.
(S390_NUM_SPILL_SLOTS): New macro.
(s390_get_frame_info): Rewritten.
(is_arg_reg): Deleted.
Break out the decoding of S/390 instructions into separate
functions, to make it more legible, and easier to check
against the spec.
* s390-tdep.c (is_ri, is_ril, is_rr, is_rre, is_rs, is_rse,
is_rx, is_rxe): New functions.
(op1_aghi, op2_aghi, op1_ahi, op2_ahi, op_ar, op_basr, op1_bras,
op2_bras, op_l, op_la, op1_larl, op2_larl, op_lgr, op1_lghi,
op2_lghi, op1_lhi, op2_lhi, op_lr, op_nr, op_ngr, op_s, op_st,
op_std, op1_stg, op2_stg, op_stm, op1_stmg, op2_stmg, op_svc): New
enums for opcode values. (Is this an improvement?)
*** s390-tdep.c.~1.92.~ 2003-04-18 15:21:07.000000000 -0500
--- s390-tdep.c 2003-05-04 17:12:43.000000000 -0500
***************
*** 187,677 ****
}
! /* Return true if REGIDX is the number of a register used to pass
! arguments, false otherwise. */
! static int
! is_arg_reg (int regidx)
{
! return 2 <= regidx && regidx <= 6;
}
! /* s390_get_frame_info based on Hartmuts
! prologue definition in
! gcc-2.8.1/config/l390/linux.c
- It reads one instruction at a time & based on whether
- it looks like prologue code or not it makes a decision on
- whether the prologue is over, there are various state machines
- in the code to determine if the prologue code is possilby valid.
-
- This is done to hopefully allow the code survive minor revs of
- calling conventions.
! */
! int
! s390_get_frame_info (CORE_ADDR pc, struct frame_extra_info *fextra_info,
! struct frame_info *fi, int init_extra_info)
{
! #define CONST_POOL_REGIDX 13
! #define GOT_REGIDX 12
! bfd_byte instr[S390_MAX_INSTR_SIZE];
! CORE_ADDR test_pc = pc, test_pc2;
! CORE_ADDR orig_sp = 0, save_reg_addr = 0, *saved_regs = NULL;
! int valid_prologue, good_prologue = 0;
! int gprs_saved[S390_NUM_GPRS];
! int fprs_saved[S390_NUM_FPRS];
! int regidx, instrlen;
! int const_pool_state;
! int varargs_state;
! int loop_cnt, gdb_gpr_store, gdb_fpr_store;
! int offset, expected_offset;
! int err = 0;
! disassemble_info info;
- /* Have we seen an instruction initializing the frame pointer yet?
- If we've seen an `lr %r11, %r15', then frame_pointer_found is
- non-zero, and frame_pointer_regidx == 11. Otherwise,
- frame_pointer_found is zero and frame_pointer_regidx is 15,
- indicating that we're using the stack pointer as our frame
- pointer. */
- int frame_pointer_found = 0;
- int frame_pointer_regidx = 0xf;
-
- /* What we've seen so far regarding saving the back chain link:
- 0 -- nothing yet; sp still has the same value it had at the entry
- point. Since not all functions allocate frames, this is a
- valid state for the prologue to finish in.
- 1 -- We've saved the original sp in some register other than the
- frame pointer (hard-coded to be %r11, yuck).
- save_link_regidx is the register we saved it in.
- 2 -- We've seen the initial `bras' instruction of the sequence for
- reserving more than 32k of stack:
- bras %rX, .+8
- .long N
- s %r15, 0(%rX)
- where %rX is not the constant pool register.
- subtract_sp_regidx is %rX, and fextra_info->stack_bought is N.
- 3 -- We've reserved space for a new stack frame. This means we
- either saw a simple `ahi %r15,-N' in state 1, or the final
- `s %r15, ...' in state 2.
- 4 -- The frame and link are now fully initialized. We've
- reserved space for the new stack frame, and stored the old
- stack pointer captured in the back chain pointer field. */
- int save_link_state = 0;
- int save_link_regidx, subtract_sp_regidx;
-
- /* What we've seen so far regarding r12 --- the GOT (Global Offset
- Table) pointer. We expect to see `l %r12, N(%r13)', which loads
- r12 with the offset from the constant pool to the GOT, and then
- an `ar %r12, %r13', which adds the constant pool address,
- yielding the GOT's address. Here's what got_state means:
- 0 -- seen nothing
- 1 -- seen `l %r12, N(%r13)', but no `ar'
- 2 -- seen load and add, so GOT pointer is totally initialized
- When got_state is 1, then got_load_addr is the address of the
- load instruction, and got_load_len is the length of that
- instruction. */
- int got_state= 0;
- CORE_ADDR got_load_addr = 0, got_load_len = 0;
! const_pool_state = varargs_state = 0;
- memset (gprs_saved, 0, sizeof (gprs_saved));
- memset (fprs_saved, 0, sizeof (fprs_saved));
- info.read_memory_func = dis_asm_read_memory;
! save_link_regidx = subtract_sp_regidx = 0;
! if (fextra_info)
{
! if (fi && get_frame_base (fi))
! {
! orig_sp = get_frame_base (fi);
! if (! init_extra_info && fextra_info->initialised)
! orig_sp += fextra_info->stack_bought;
! saved_regs = get_frame_saved_regs (fi);
! }
! if (init_extra_info || !fextra_info->initialised)
! {
! s390_memset_extra_info (fextra_info);
! fextra_info->function_start = pc;
! fextra_info->initialised = 1;
! }
}
! instrlen = 0;
! do
{
! valid_prologue = 0;
! test_pc += instrlen;
! /* add the previous instruction len */
! instrlen = s390_readinstruction (instr, test_pc, &info);
! if (instrlen < 0)
! {
! good_prologue = 0;
! err = -1;
! break;
! }
! /* We probably are in a glibc syscall */
! if (instr[0] == S390_SYSCALL_OPCODE && test_pc == pc)
! {
! good_prologue = 1;
! if (saved_regs && fextra_info && get_next_frame (fi)
! && get_frame_extra_info (get_next_frame (fi))
! && get_frame_extra_info (get_next_frame (fi))->sigcontext)
! {
! /* We are backtracing from a signal handler */
! save_reg_addr = get_frame_extra_info (get_next_frame (fi))->sigcontext +
! REGISTER_BYTE (S390_GP0_REGNUM);
! for (regidx = 0; regidx < S390_NUM_GPRS; regidx++)
! {
! saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
! save_reg_addr += S390_GPR_SIZE;
! }
! save_reg_addr = get_frame_extra_info (get_next_frame (fi))->sigcontext +
! (GDB_TARGET_IS_ESAME ? S390X_SIGREGS_FP0_OFFSET :
! S390_SIGREGS_FP0_OFFSET);
! for (regidx = 0; regidx < S390_NUM_FPRS; regidx++)
! {
! saved_regs[S390_FP0_REGNUM + regidx] = save_reg_addr;
! save_reg_addr += S390_FPR_SIZE;
! }
! }
! break;
! }
! if (save_link_state == 0)
! {
! /* check for a stack relative STMG or STM */
! if (((GDB_TARGET_IS_ESAME &&
! ((instr[0] == 0xeb) && (instr[5] == 0x24))) ||
! (instr[0] == 0x90)) && ((instr[2] >> 4) == 0xf))
! {
! regidx = (instr[1] >> 4);
! if (regidx < 6)
! varargs_state = 1;
! offset = ((instr[2] & 0xf) << 8) + instr[3];
! expected_offset =
! S390_GPR6_STACK_OFFSET + (S390_GPR_SIZE * (regidx - 6));
! if (offset != expected_offset)
! {
! good_prologue = 0;
! break;
! }
! if (saved_regs)
! save_reg_addr = orig_sp + offset;
! for (; regidx <= (instr[1] & 0xf); regidx++)
! {
! if (gprs_saved[regidx])
! {
! good_prologue = 0;
! break;
! }
! good_prologue = 1;
! gprs_saved[regidx] = 1;
! if (saved_regs)
! {
! saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
! save_reg_addr += S390_GPR_SIZE;
! }
! }
! valid_prologue = 1;
! continue;
! }
! }
! /* check for a stack relative STG or ST */
! if ((save_link_state == 0 || save_link_state == 3) &&
! ((GDB_TARGET_IS_ESAME &&
! ((instr[0] == 0xe3) && (instr[5] == 0x24))) ||
! (instr[0] == 0x50)) && ((instr[2] >> 4) == 0xf))
! {
! regidx = instr[1] >> 4;
! offset = ((instr[2] & 0xf) << 8) + instr[3];
! if (offset == 0)
! {
! if (save_link_state == 3 && regidx == save_link_regidx)
! {
! save_link_state = 4;
! valid_prologue = 1;
! continue;
! }
! else
! break;
! }
! if (regidx < 6)
! varargs_state = 1;
! expected_offset =
! S390_GPR6_STACK_OFFSET + (S390_GPR_SIZE * (regidx - 6));
! if (offset != expected_offset)
! {
! good_prologue = 0;
! break;
! }
! if (gprs_saved[regidx])
! {
! good_prologue = 0;
! break;
! }
! good_prologue = 1;
! gprs_saved[regidx] = 1;
! if (saved_regs)
! {
! save_reg_addr = orig_sp + offset;
! saved_regs[S390_GP0_REGNUM + regidx] = save_reg_addr;
! }
! valid_prologue = 1;
! continue;
! }
! /* Check for an fp-relative STG, ST, or STM. This is probably
! spilling an argument from a register out into a stack slot.
! This could be a user instruction, but if we haven't included
! any other suspicious instructions in the prologue, this
! could only be an initializing store, which isn't too bad to
! skip. The consequences of not including arg-to-stack spills
! are more serious, though --- you don't see the proper values
! of the arguments. */
! if ((save_link_state == 3 || save_link_state == 4)
! && ((instr[0] == 0x50 /* st %rA, D(%rX,%rB) */
! && (instr[1] & 0xf) == 0 /* %rX is zero, no index reg */
! && is_arg_reg ((instr[1] >> 4) & 0xf)
! && ((instr[2] >> 4) & 0xf) == frame_pointer_regidx)
! || (instr[0] == 0x90 /* stm %rA, %rB, D(%rC) */
! && is_arg_reg ((instr[1] >> 4) & 0xf)
! && is_arg_reg (instr[1] & 0xf)
! && ((instr[2] >> 4) & 0xf) == frame_pointer_regidx)))
{
! valid_prologue = 1;
! continue;
}
- /* check for STD */
- if (instr[0] == 0x60 && (instr[2] >> 4) == 0xf)
- {
- regidx = instr[1] >> 4;
- if (regidx == 0 || regidx == 2)
- varargs_state = 1;
- if (fprs_saved[regidx])
- {
- good_prologue = 0;
- break;
- }
- fprs_saved[regidx] = 1;
- if (saved_regs)
- {
- save_reg_addr = orig_sp + (((instr[2] & 0xf) << 8) + instr[3]);
- saved_regs[S390_FP0_REGNUM + regidx] = save_reg_addr;
- }
- valid_prologue = 1;
- continue;
- }
! if (const_pool_state == 0)
! {
! if (GDB_TARGET_IS_ESAME)
! {
! /* Check for larl CONST_POOL_REGIDX,offset on ESAME */
! if ((instr[0] == 0xc0)
! && (instr[1] == (CONST_POOL_REGIDX << 4)))
! {
! const_pool_state = 2;
! valid_prologue = 1;
! continue;
! }
! }
! else
! {
! /* Check for BASR gpr13,gpr0 used to load constant pool pointer to r13 in old compiler */
! if (instr[0] == 0xd && (instr[1] & 0xf) == 0
! && ((instr[1] >> 4) == CONST_POOL_REGIDX))
! {
! const_pool_state = 1;
! valid_prologue = 1;
! continue;
! }
! }
! /* Check for new fangled bras %r13,newpc to load new constant pool */
! /* embedded in code, older pre abi compilers also emitted this stuff. */
! if ((instr[0] == 0xa7) && ((instr[1] & 0xf) == 0x5) &&
! ((instr[1] >> 4) == CONST_POOL_REGIDX)
! && ((instr[2] & 0x80) == 0))
! {
! const_pool_state = 2;
! test_pc +=
! (((((instr[2] & 0xf) << 8) + instr[3]) << 1) - instrlen);
! valid_prologue = 1;
! continue;
! }
! }
! /* Check for AGHI or AHI CONST_POOL_REGIDX,val */
! if (const_pool_state == 1 && (instr[0] == 0xa7) &&
! ((GDB_TARGET_IS_ESAME &&
! (instr[1] == ((CONST_POOL_REGIDX << 4) | 0xb))) ||
! (instr[1] == ((CONST_POOL_REGIDX << 4) | 0xa))))
! {
! const_pool_state = 2;
! valid_prologue = 1;
! continue;
! }
! /* Check for LGR or LR gprx,15 */
! if ((GDB_TARGET_IS_ESAME &&
! instr[0] == 0xb9 && instr[1] == 0x04 && (instr[3] & 0xf) == 0xf) ||
! (instr[0] == 0x18 && (instr[1] & 0xf) == 0xf))
! {
! if (GDB_TARGET_IS_ESAME)
! regidx = instr[3] >> 4;
! else
! regidx = instr[1] >> 4;
! if (save_link_state == 0 && regidx != 0xb)
! {
! /* Almost defintely code for
! decrementing the stack pointer
! ( i.e. a non leaf function
! or else leaf with locals ) */
! save_link_regidx = regidx;
! save_link_state = 1;
! valid_prologue = 1;
! continue;
! }
! /* We use this frame pointer for alloca
! unfortunately we need to assume its gpr11
! otherwise we would need a smarter prologue
! walker. */
! if (!frame_pointer_found && regidx == 0xb)
! {
! frame_pointer_regidx = 0xb;
! frame_pointer_found = 1;
! if (fextra_info)
! fextra_info->frame_pointer_saved_pc = test_pc;
! valid_prologue = 1;
! continue;
! }
! }
! /* Check for AHI or AGHI gpr15,val */
! if (save_link_state == 1 && (instr[0] == 0xa7) &&
! ((GDB_TARGET_IS_ESAME && (instr[1] == 0xfb)) || (instr[1] == 0xfa)))
! {
! if (fextra_info)
! fextra_info->stack_bought =
! -extract_signed_integer (&instr[2], 2);
! save_link_state = 3;
! valid_prologue = 1;
! continue;
! }
! /* Alternatively check for the complex construction for
! buying more than 32k of stack
! BRAS gprx,.+8
! long val
! s %r15,0(%gprx) gprx currently r1 */
! if ((save_link_state == 1) && (instr[0] == 0xa7)
! && ((instr[1] & 0xf) == 0x5) && (instr[2] == 0)
! && (instr[3] == 0x4) && ((instr[1] >> 4) != CONST_POOL_REGIDX))
! {
! subtract_sp_regidx = instr[1] >> 4;
! save_link_state = 2;
! if (fextra_info)
! target_read_memory (test_pc + instrlen,
! (char *) &fextra_info->stack_bought,
! sizeof (fextra_info->stack_bought));
! test_pc += 4;
! valid_prologue = 1;
! continue;
! }
! if (save_link_state == 2 && instr[0] == 0x5b
! && instr[1] == 0xf0 &&
! instr[2] == (subtract_sp_regidx << 4) && instr[3] == 0)
! {
! save_link_state = 3;
! valid_prologue = 1;
! continue;
! }
! /* check for LA gprx,offset(15) used for varargs */
! if ((instr[0] == 0x41) && ((instr[2] >> 4) == 0xf) &&
! ((instr[1] & 0xf) == 0))
! {
! /* some code uses gpr7 to point to outgoing args */
! if (((instr[1] >> 4) == 7) && (save_link_state == 0) &&
! ((instr[2] & 0xf) == 0)
! && (instr[3] == S390_STACK_FRAME_OVERHEAD))
! {
! valid_prologue = 1;
! continue;
! }
! if (varargs_state == 1)
! {
! varargs_state = 2;
! valid_prologue = 1;
! continue;
! }
! }
! /* Check for a GOT load */
! if (GDB_TARGET_IS_ESAME)
! {
! /* Check for larl GOT_REGIDX, on ESAME */
! if ((got_state == 0) && (instr[0] == 0xc0)
! && (instr[1] == (GOT_REGIDX << 4)))
! {
! got_state = 2;
! valid_prologue = 1;
! continue;
! }
! }
! else
! {
! /* check for l GOT_REGIDX,x(CONST_POOL_REGIDX) */
! if (got_state == 0 && const_pool_state == 2 && instr[0] == 0x58
! && (instr[2] == (CONST_POOL_REGIDX << 4))
! && ((instr[1] >> 4) == GOT_REGIDX))
! {
! got_state = 1;
! got_load_addr = test_pc;
! got_load_len = instrlen;
! valid_prologue = 1;
! continue;
! }
! /* Check for subsequent ar got_regidx,basr_regidx */
! if (got_state == 1 && instr[0] == 0x1a &&
! instr[1] == ((GOT_REGIDX << 4) | CONST_POOL_REGIDX))
! {
! got_state = 2;
! valid_prologue = 1;
! continue;
! }
! }
}
! while (valid_prologue && good_prologue);
! if (good_prologue)
{
! /* If this function doesn't reference the global offset table,
! then the compiler may use r12 for other things. If the last
! instruction we saw was a load of r12 from the constant pool,
! with no subsequent add to make the address PC-relative, then
! the load was probably a genuine body instruction; don't treat
! it as part of the prologue. */
! if (got_state == 1
! && got_load_addr + got_load_len == test_pc)
{
! test_pc = got_load_addr;
! instrlen = got_load_len;
}
! good_prologue = (((const_pool_state == 0) || (const_pool_state == 2)) &&
! ((save_link_state == 0) || (save_link_state == 4)) &&
! ((varargs_state == 0) || (varargs_state == 2)));
! }
! if (fextra_info)
! {
! fextra_info->good_prologue = good_prologue;
! fextra_info->skip_prologue_function_start =
! (good_prologue ? test_pc : pc);
! }
! if (saved_regs)
! /* The SP's element of the saved_regs array holds the old SP,
! not the address at which it is saved. */
! saved_regs[S390_SP_REGNUM] = orig_sp;
! return err;
}
--- 187,1482 ----
}
! /* Prologue analysis. */
!
! /* When we analyze a prologue, we're really doing 'abstract
! interpretation' or 'pseudo-evaluation': running the function's code
! in simulation, but using conservative approximations of the values
! it would have when it actually runs. For example, if our function
! starts with the instruction:
!
! ahi r1, 42 # add halfword immediate 42 to r1
!
! we don't know exactly what value will be in r1 after executing this
! instruction, but we do know it'll be 42 greater than its original
! value.
!
! If we then see an instruction like:
!
! ahi r1, 22 # add halfword immediate 22 to r1
!
! we still don't know what r1's value is, but again, we can say it is
! now 64 greater than its original value.
!
! If the next instruction were:
!
! lr r2, r1 # set r2 to r1's value
!
! then we can say that r2's value is now the original value of r1
! plus 64. And so on.
!
! Of course, this can only go so far before it gets unreasonable. If
! we wanted to be able to say anything about the value of r1 after
! the instruction:
!
! xr r1, r3 # exclusive-or r1 and r3, place result in r1
!
! then things would get pretty complex. But remember, we're just
! doing a conservative approximation; if exclusive-or instructions
! aren't relevant to prologues, we can just say r1's value is now
! 'unknown'. We can ignore things that are too complex, if that loss
! of information is acceptable for our application.
!
! Once you've reached an instruction that you don't know how to
! simulate, you stop. Now you examine the state of the registers and
! stack slots you've kept track of. For example:
!
! - To see how large your stack frame is, just check the value of sp;
! if it's the original value of sp minus a constant, then that
! constant is the stack frame's size. If the sp's value has been
! marked as 'unknown', then that means the prologue has done
! something too complex for us to track, and we don't know the
! frame size.
!
! - To see whether we've saved the SP in the current frame's back
! chain slot, we just check whether the current value of the back
! chain stack slot is the original value of the sp.
!
! Sure, this takes some work. But prologue analyzers aren't
! quick-and-simple pattern patching to recognize a few fixed prologue
! forms any more; they're big, hairy functions. Along with inferior
! function calls, prologue analysis accounts for a substantial
! portion of the time needed to stabilize a GDB port. So I think
! it's worthwhile to look for an approach that will be easier to
! understand and maintain. In the approach used here:
!
! - It's easier to see that the analyzer is correct: you just see
! whether the analyzer properly (albiet conservatively) simulates
! the effect of each instruction.
!
! - It's easier to extend the analyzer: you can add support for new
! instructions, and know that you haven't broken anything that
! wasn't already broken before.
!
! - It's orthogonal: to gather new information, you don't need to
! complicate the code for each instruction. As long as your domain
! of conservative values is already detailed enough to tell you
! what you need, then all the existing instruction simulations are
! already gathering the right data for you.
!
! A 'struct prologue_value' is a conservative approximation of the
! real value the register or stack slot will have. */
!
! struct prologue_value {
!
! /* What sort of value is this? This determines the interpretation
! of subsequent fields. */
! enum {
!
! /* We don't know anything about the value. This is also used for
! values we could have kept track of, when doing so would have
! been too complex and we don't want to bother. The bottom of
! our lattice. */
! pv_unknown,
!
! /* A known constant. K is its value. */
! pv_constant,
!
! /* The value that register REG originally had *UPON ENTRY TO THE
! FUNCTION*, plus K. If K is zero, this means, obviously, just
! the value REG had upon entry to the function. REG is a GDB
! register number. Before we start interpreting, we initialize
! every register R to { pv_register, R, 0 }. */
! pv_register,
!
! } kind;
!
! /* The meanings of the following fields depend on 'kind'; see the
! comments for the specific 'kind' values. */
! int reg;
! CORE_ADDR k;
! };
!
!
! /* Set V to be unknown. */
! static void
! pv_set_to_unknown (struct prologue_value *v)
{
! v->kind = pv_unknown;
}
! /* Set V to the constant K. */
! static void
! pv_set_to_constant (struct prologue_value *v, CORE_ADDR k)
! {
! v->kind = pv_constant;
! v->k = k;
! }
! /* Set V to the original value of register REG, plus K. */
! static void
! pv_set_to_register (struct prologue_value *v, int reg, CORE_ADDR k)
! {
! v->kind = pv_register;
! v->reg = reg;
! v->k = k;
! }
!
! /* If one of *A and *B is a constant, and the other isn't, swap the
! pointers as necessary to ensure that *B points to the constant.
! This can reduce the number of cases we need to analyze in the
! functions below. */
! static void
! pv_constant_last (struct prologue_value **a,
! struct prologue_value **b)
{
! if ((*a)->kind == pv_constant
! && (*b)->kind != pv_constant)
! {
! struct prologue_value *temp = *a;
! *a = *b;
! *b = temp;
! }
! }
! /* Set SUM to the sum of A and B. SUM, A, and B may point to the same
! 'struct prologue_value' object. */
! static void
! pv_add (struct prologue_value *sum,
! struct prologue_value *a,
! struct prologue_value *b)
! {
! pv_constant_last (&a, &b);
!
! /* We can handle adding constants to registers, and other constants. */
! if (b->kind == pv_constant
! && (a->kind == pv_register
! || a->kind == pv_constant))
! {
! sum->kind = a->kind;
! sum->reg = a->reg; /* not meaningful if a is pv_constant, but
! harmless */
! sum->k = a->k + b->k;
! }
!
! /* Anything else we don't know how to add. We don't have a
! representation for, say, the sum of two registers, or a multiple
! of a register's value (adding a register to itself). */
! else
! sum->kind = pv_unknown;
! }
! /* Add the constant K to V. */
! static void
! pv_add_constant (struct prologue_value *v, CORE_ADDR k)
! {
! struct prologue_value pv_k;
!
! /* Rather than thinking of all the cases we can and can't handle,
! we'll just let pv_add take care of that for us. */
! pv_set_to_constant (&pv_k, k);
! pv_add (v, v, &pv_k);
! }
!
!
! /* Subtract B from A, and put the result in DIFF.
!
! This isn't quite the same as negating B and adding it to A, since
! we don't have a representation for the negation of anything but a
! constant. For example, we can't negate { pv_register, R1, 10 },
! but we do know that { pv_register, R1, 10 } minus { pv_register,
! R1, 5 } is { pv_constant, <ignored>, 5 }.
!
! This means, for example, that we can subtract two stack addresses;
! they're both relative to the original SP. Since the frame pointer
! is set based on the SP, its value will be the original SP plus some
! constant (probably zero), so we can use its value just fine. */
! static void
! pv_subtract (struct prologue_value *diff,
! struct prologue_value *a,
! struct prologue_value *b)
! {
! pv_constant_last (&a, &b);
!
! /* We can subtract a constant from another constant, or from a
! register. */
! if (b->kind == pv_constant
! && (a->kind == pv_register
! || a->kind == pv_constant))
{
! diff->kind = a->kind;
! diff->reg = a->reg; /* not always meaningful, but harmless */
! diff->k = a->k - b->k;
}
!
! /* We can subtract a register from itself, yielding a constant. */
! else if (a->kind == pv_register
! && b->kind == pv_register
! && a->reg == b->reg)
{
! diff->kind = pv_constant;
! diff->k = a->k - b->k;
! }
!
! /* We don't know how to subtract anything else. */
! else
! diff->kind = pv_unknown;
! }
!
! /* Set AND to the logical and of A and B. */
! static void
! pv_logical_and (struct prologue_value *and,
! struct prologue_value *a,
! struct prologue_value *b)
! {
! pv_constant_last (&a, &b);
!
! /* We can 'and' two constants. */
! if (a->kind == pv_constant
! && b->kind == pv_constant)
! {
! and->kind = pv_constant;
! and->k = a->k & b->k;
! }
!
! /* We can 'and' anything with the constant zero. */
! else if (b->kind == pv_constant
! && b->k == 0)
! {
! and->kind = pv_constant;
! and->k = 0;
! }
!
! /* We can 'and' anything with ~0. */
! else if (b->kind == pv_constant
! && b->k == ~ (CORE_ADDR) 0)
! *and = *a;
!
! /* We can 'and' a register with itself. */
! else if (a->kind == pv_register
! && b->kind == pv_register
! && a->reg == b->reg
! && a->k == b->k)
! *and = *a;
!
! /* Otherwise, we don't know. */
! else
! pv_set_to_unknown (and);
! }
!
!
! /* Return non-zero iff A and B are identical expressions.
!
! This is not the same as asking if the two values are equal; the
! result of such a comparison would have to be a pv_boolean, and
! asking whether two 'unknown' values were equal would give you
! pv_maybe. Same for comparing, say, { pv_register, R1, 0 } and {
! pv_register, R2, 0}. Instead, this is asking whether the two
! representations are the same. */
! static int
! pv_is_identical (struct prologue_value *a,
! struct prologue_value *b)
! {
! if (a->kind != b->kind)
! return 0;
!
! switch (a->kind)
! {
! case pv_unknown:
! return 1;
! case pv_constant:
! return (a->k == b->k);
! case pv_register:
! return (a->reg == b->reg && a->k == b->k);
! default:
! gdb_assert (0);
! }
! }
!
!
! /* Return non-zero if A is the original value of register number R
! plus K, zero otherwise. */
! static int
! pv_is_register (struct prologue_value *a, int r, CORE_ADDR k)
! {
! return (a->kind == pv_register
! && a->reg == r
! && a->k == k);
! }
!
!
! /* A prologue-value-esque boolean type, including "maybe", when we
! can't figure out whether something is true or not. */
! enum pv_boolean {
! pv_maybe,
! pv_definite_yes,
! pv_definite_no,
! };
!
!
! /* Decide whether a reference to SIZE bytes at ADDR refers exactly to
! an element of an array. The array starts at ARRAY_ADDR, and has
! ARRAY_LEN values of ELT_SIZE bytes each. If ADDR definitely does
! refer to an array element, set *I to the index of the referenced
! element in the array, and return pv_definite_yes. If it definitely
! doesn't, return pv_definite_no. If we can't tell, return pv_maybe.
!
! If the reference does touch the array, but doesn't fall exactly on
! an element boundary, or doesn't refer to the whole element, return
! pv_maybe. */
! static enum pv_boolean
! pv_is_array_ref (struct prologue_value *addr,
! CORE_ADDR size,
! struct prologue_value *array_addr,
! CORE_ADDR array_len,
! CORE_ADDR elt_size,
! int *i)
! {
! struct prologue_value offset;
!
! /* Note that, since ->k is a CORE_ADDR, and CORE_ADDR is unsigned,
! if addr is *before* the start of the array, then this isn't going
! to be negative... */
! pv_subtract (&offset, addr, array_addr);
!
! if (offset.kind == pv_constant)
! {
! /* This is a rather odd test. We want to know if the SIZE bytes
! at ADDR don't overlap the array at all, so you'd expect it to
! be an || expression: "if we're completely before || we're
! completely after". But with unsigned arithmetic, things are
! different: since it's a number circle, not a number line, the
! right values for offset.k are actually one contiguous range. */
! if (offset.k <= -size
! && offset.k >= array_len * elt_size)
! return pv_definite_no;
! else if (offset.k % elt_size != 0
! || size != elt_size)
! return pv_maybe;
! else
{
! *i = offset.k / elt_size;
! return pv_definite_yes;
}
+ }
+ else
+ return pv_maybe;
+ }
! /* Decoding S/390 instructions. */
! /* Named opcode values for the S/390 instructions we recognize. Some
! instructions have their opcode split across two fields; those are the
! op1_* and op2_* enums. */
! enum
! {
! op1_aghi = 0xa7, op2_aghi = 0xb,
! op1_ahi = 0xa7, op2_ahi = 0xa,
! op_ar = 0x1a,
! op_basr = 0x0d,
! op1_bras = 0xa7, op2_bras = 0x5,
! op_l = 0x58,
! op_la = 0x41,
! op1_larl = 0xc0, op2_larl = 0x0,
! op_lgr = 0xb904,
! op1_lghi = 0xa7, op2_lghi = 0x9,
! op1_lhi = 0xa7, op2_lhi = 0x8,
! op_lr = 0x18,
! op_nr = 0x14,
! op_ngr = 0xb980,
! op_s = 0x5b,
! op_st = 0x50,
! op_std = 0x60,
! op1_stg = 0xe3, op2_stg = 0x24,
! op_stm = 0x90,
! op1_stmg = 0xeb, op2_stmg = 0x24,
! op_svc = 0x0a,
! };
!
!
! /* The functions below are for recognizing and decoding S/390
! instructions of various formats. Each of them checks whether INSN
! is an instruction of the given format, with the specified opcodes.
! If it is, it sets the remaining arguments to the values of the
! instruction's fields, and returns a non-zero value; otherwise, it
! returns zero.
!
! These functions' arguments appear in the order they appear in the
! instruction, not in the machine-language form. So, opcodes always
! come first, even though they're sometimes scattered around the
! instructions. And displacements appear before base and extension
! registers, as they do in the assembly syntax, not at the end, as
! they do in the machine language. */
! static int
! is_ri (bfd_byte *insn, int op1, int op2, unsigned int *r1, int *i2)
! {
! if (insn[0] == op1 && (insn[1] & 0xf) == op2)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! /* i2 is a 16-bit signed quantity. */
! *i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
! return 1;
! }
! else
! return 0;
! }
!
! static int
! is_ril (bfd_byte *insn, int op1, int op2,
! unsigned int *r1, int *i2)
! {
! if (insn[0] == op1 && (insn[1] & 0xf) == op2)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! /* i2 is a signed quantity. If the host 'int' is 32 bits long,
! no sign extension is necessary, but we don't want to assume
! that. */
! *i2 = (((insn[2] << 24)
! | (insn[3] << 16)
! | (insn[4] << 8)
! | (insn[5])) ^ 0x80000000) - 0x80000000;
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rr (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
! {
! if (insn[0] == op)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *r2 = insn[1] & 0xf;
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rre (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
! {
! if (((insn[0] << 8) | insn[1]) == op)
! {
! /* Yes, insn[3]. insn[2] is unused in RRE format. */
! *r1 = (insn[3] >> 4) & 0xf;
! *r2 = insn[3] & 0xf;
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rs (bfd_byte *insn, int op,
! unsigned int *r1, unsigned int *r3, unsigned int *d2, unsigned int *b2)
! {
! if (insn[0] == op)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *r3 = insn[1] & 0xf;
! *b2 = (insn[2] >> 4) & 0xf;
! *d2 = ((insn[2] & 0xf) << 8) | insn[3];
! return 1;
}
! else
! return 0;
! }
!
!
! static int
! is_rse (bfd_byte *insn, int op1, int op2,
! unsigned int *r1, unsigned int *r3, unsigned int *d2, unsigned int *b2)
! {
! if (insn[0] == op1
! /* Yes, insn[5]. insn[4] is unused. */
! && insn[5] == op2)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *r3 = insn[1] & 0xf;
! *b2 = (insn[2] >> 4) & 0xf;
! *d2 = ((insn[2] & 0xf) << 8) | insn[3];
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rx (bfd_byte *insn, int op,
! unsigned int *r1, unsigned int *d2, unsigned int *x2, unsigned int *b2)
! {
! if (insn[0] == op)
{
! *r1 = (insn[1] >> 4) & 0xf;
! *x2 = insn[1] & 0xf;
! *b2 = (insn[2] >> 4) & 0xf;
! *d2 = ((insn[2] & 0xf) << 8) | insn[3];
! return 1;
! }
! else
! return 0;
! }
!
!
! static int
! is_rxe (bfd_byte *insn, int op1, int op2,
! unsigned int *r1, unsigned int *d2, unsigned int *x2, unsigned int *b2)
! {
! if (insn[0] == op1
! /* Yes, insn[5]. insn[4] is unused. */
! && insn[5] == op2)
! {
! *r1 = (insn[1] >> 4) & 0xf;
! *x2 = insn[1] & 0xf;
! *b2 = (insn[2] >> 4) & 0xf;
! *d2 = ((insn[2] & 0xf) << 8) | insn[3];
! return 1;
! }
! else
! return 0;
! }
!
!
! /* Set ADDR to the effective address for an X-style instruction, like:
!
! L R1, D2(X2, B2)
!
! Here, X2 and B2 are registers, and D2 is an unsigned 12-bit
! constant; the effective address is the sum of all three. If either
! X2 or B2 are zero, then it doesn't contribute to the sum --- this
! means that r0 can't be used as either X2 or B2.
!
! GPR is an array of general register values, indexed by GPR number,
! not GDB register number. */
! static void
! compute_x_addr (struct prologue_value *addr,
! struct prologue_value *gpr,
! unsigned int d2, unsigned int x2, unsigned int b2)
! {
! /* We can't just add stuff directly in addr; it might alias some of
! the registers we need to read. */
! struct prologue_value result;
!
! pv_set_to_constant (&result, d2);
! if (x2)
! pv_add (&result, &result, &gpr[x2]);
! if (b2)
! pv_add (&result, &result, &gpr[b2]);
!
! *addr = result;
! }
!
!
! /* The number of GPR and FPR spill slots in an S/390 stack frame. We
! track general-purpose registers r2 -- r15, and floating-point
! registers f0, f2, f4, and f6. */
! #define S390_NUM_SPILL_SLOTS (14 + 4)
!
!
! /* If the SIZE bytes at ADDR are a stack slot we're actually tracking,
! return pv_definite_yes and set *STACK to point to the slot. If
! we're sure that they are not any of our stack slots, then return
! pv_definite_no. Otherwise, return pv_maybe.
! - GPR is an array indexed by GPR number giving the current values
! of the general-purpose registers.
! - SPILL is an array tracking the spill area of the caller's frame;
! SPILL[i] is the i'th spill slot. The spill slots are designated
! for r2 -- r15, and then f0, f2, f4, and f6.
! - BACK_CHAIN is the value of the back chain slot; it's only valid
! when the current frame actually has some space for a back chain
! slot --- that is, when the current value of the stack pointer
! (according to GPR) is at least S390_STACK_FRAME_OVERHEAD bytes
! less than its original value. */
! static enum pv_boolean
! s390_on_stack (struct prologue_value *addr,
! CORE_ADDR size,
! struct prologue_value *gpr,
! struct prologue_value *spill,
! struct prologue_value *back_chain,
! struct prologue_value **stack)
! {
! struct prologue_value gpr_spill_addr;
! struct prologue_value fpr_spill_addr;
! struct prologue_value back_chain_addr;
! int i;
! enum pv_boolean b;
!
! /* Construct the addresses of the spill arrays and the back chain. */
! pv_set_to_register (&gpr_spill_addr, S390_SP_REGNUM, 2 * S390_GPR_SIZE);
! pv_set_to_register (&fpr_spill_addr, S390_SP_REGNUM, 16 * S390_GPR_SIZE);
! back_chain_addr = gpr[S390_SP_REGNUM - S390_GP0_REGNUM];
!
! /* We have to check for GPR and FPR references using two separate
! calls to pv_is_array_ref, since the GPR and FPR spill slots are
! different sizes. (SPILL is an array, but the thing it tracks
! isn't really an array.) */
!
! /* Was it a reference to the GPR spill array? */
! b = pv_is_array_ref (addr, size, &gpr_spill_addr, 14, S390_GPR_SIZE, &i);
! if (b == pv_definite_yes)
! {
! *stack = &spill[i];
! return pv_definite_yes;
! }
! if (b == pv_maybe)
! return pv_maybe;
!
! /* Was it a reference to the FPR spill array? */
! b = pv_is_array_ref (addr, size, &fpr_spill_addr, 4, S390_FPR_SIZE, &i);
! if (b == pv_definite_yes)
! {
! *stack = &spill[14 + i];
! return pv_definite_yes;
! }
! if (b == pv_maybe)
! return pv_maybe;
!
! /* Was it a reference to the back chain?
! This isn't quite right. We ought to check whether we have
! actually allocated any new frame at all. */
! b = pv_is_array_ref (addr, size, &back_chain_addr, 1, S390_GPR_SIZE, &i);
! if (b == pv_definite_yes)
! {
! *stack = back_chain;
! return pv_definite_yes;
! }
! if (b == pv_maybe)
! return pv_maybe;
!
! /* All the above queries returned definite 'no's. */
! return pv_definite_no;
! }
!
!
! /* Do a SIZE-byte store of VALUE to ADDR. GPR, SPILL, and BACK_CHAIN,
! and the return value are as described for s390_on_stack, above.
! Note that, when this returns pv_maybe, we have to assume that all
! of our memory now contains unknown values. */
! static enum pv_boolean
! s390_store (struct prologue_value *addr,
! CORE_ADDR size,
! struct prologue_value *value,
! struct prologue_value *gpr,
! struct prologue_value *spill,
! struct prologue_value *back_chain)
! {
! struct prologue_value *stack;
! enum pv_boolean on_stack
! = s390_on_stack (addr, size, gpr, spill, back_chain, &stack);
!
! if (on_stack == pv_definite_yes)
! *stack = *value;
!
! return on_stack;
! }
!
!
! /* The current frame looks like a signal delivery frame: the first
! instruction is an 'svc' opcode. If the next frame is a signal
! handler's frame, set FI's saved register map to point into the
! signal context structure. */
! static void
! s390_get_signal_frame_info (struct frame_info *fi)
! {
! struct frame_info *next_frame = get_next_frame (fi);
!
! if (next_frame
! && get_frame_extra_info (next_frame)
! && get_frame_extra_info (next_frame)->sigcontext)
! {
! /* We're definitely backtracing from a signal handler. */
! CORE_ADDR *saved_regs = get_frame_saved_regs (fi);
! CORE_ADDR save_reg_addr = (get_frame_extra_info (next_frame)->sigcontext
! + REGISTER_BYTE (S390_GP0_REGNUM));
! int reg;
!
! for (reg = 0; reg < S390_NUM_GPRS; reg++)
! {
! saved_regs[S390_GP0_REGNUM + reg] = save_reg_addr;
! save_reg_addr += S390_GPR_SIZE;
! }
!
! save_reg_addr = (get_frame_extra_info (next_frame)->sigcontext
! + (GDB_TARGET_IS_ESAME ? S390X_SIGREGS_FP0_OFFSET :
! S390_SIGREGS_FP0_OFFSET));
! for (reg = 0; reg < S390_NUM_FPRS; reg++)
! {
! saved_regs[S390_FP0_REGNUM + reg] = save_reg_addr;
! save_reg_addr += S390_FPR_SIZE;
! }
! }
! }
!
!
! static int
! s390_get_frame_info (CORE_ADDR start_pc,
! struct frame_extra_info *fextra_info,
! struct frame_info *fi,
! int init_extra_info)
! {
! /* Our return value:
! zero if we were able to read all the instructions we wanted, or
! -1 if we got an error trying to read memory. */
! int result = 0;
!
! /* We just use this for reading instructions. */
! disassemble_info info;
!
! /* The current PC for our abstract interpretation. */
! CORE_ADDR pc;
!
! /* The address of the next instruction after that. */
! CORE_ADDR next_pc;
!
! /* The general-purpose registers. */
! struct prologue_value gpr[S390_NUM_GPRS];
!
! /* The floating-point registers. */
! struct prologue_value fpr[S390_NUM_FPRS];
!
! /* The register spill stack slots in the caller's frame ---
! general-purpose registers r2 through r15, and floating-point
! registers. spill[i] is where gpr i+2 gets spilled;
! spill[(14, 15, 16, 17)] is where (f0, f2, f4, f6) get spilled. */
! struct prologue_value spill[S390_NUM_SPILL_SLOTS];
!
! /* The value of the back chain slot. This is only valid if the stack
! pointer is known to be less than its original value --- that is,
! if we have indeed allocated space on the stack. */
! struct prologue_value back_chain;
!
! /* The address of the instruction after the last one that changed
! the SP, FP, or back chain. */
! CORE_ADDR after_last_frame_setup_insn = start_pc;
!
! info.read_memory_func = dis_asm_read_memory;
!
! /* Set up everything's initial value. */
! {
! int i;
!
! for (i = 0; i < S390_NUM_GPRS; i++)
! pv_set_to_register (&gpr[i], S390_GP0_REGNUM + i, 0);
!
! for (i = 0; i < S390_NUM_FPRS; i++)
! pv_set_to_register (&fpr[i], S390_FP0_REGNUM + i, 0);
!
! for (i = 0; i < S390_NUM_SPILL_SLOTS; i++)
! pv_set_to_unknown (&spill[i]);
!
! pv_set_to_unknown (&back_chain);
! }
!
! /* Start interpreting instructions, until we hit something we don't
! know how to interpret. (Ideally, we should stop at the frame's
! real current PC, but at the moment, our callers don't give us
! that info.) */
! for (pc = start_pc; ; pc = next_pc)
! {
! bfd_byte insn[S390_MAX_INSTR_SIZE];
! int insn_len = s390_readinstruction (insn, pc, &info);
!
! /* Fields for various kinds of instructions. */
! unsigned int b2, r1, r2, d2, x2, r3;
! int i2;
!
! /* The values of SP, FP, and back chain before this instruction,
! for detecting instructions that change them. */
! struct prologue_value pre_insn_sp, pre_insn_fp, pre_insn_back_chain;
!
! /* If we got an error trying to read the instruction, report it. */
! if (insn_len < 0)
{
! result = -1;
! break;
! }
!
! next_pc = pc + insn_len;
!
! pre_insn_sp = gpr[S390_SP_REGNUM - S390_GP0_REGNUM];
! pre_insn_fp = gpr[S390_FRAME_REGNUM - S390_GP0_REGNUM];
! pre_insn_back_chain = back_chain;
!
! /* A special case, first --- only recognized as the very first
! instruction of the function, for signal delivery frames:
! SVC i --- system call */
! if (pc == start_pc
! && is_rr (insn, op_svc, &r1, &r2))
! {
! if (fi)
! s390_get_signal_frame_info (fi);
! break;
}
! /* AHI r1, i2 --- add halfword immediate */
! else if (is_ri (insn, op1_ahi, op2_ahi, &r1, &i2))
! pv_add_constant (&gpr[r1], i2);
!
!
! /* AGHI r1, i2 --- add halfword immediate (64-bit version) */
! else if (GDB_TARGET_IS_ESAME
! && is_ri (insn, op1_aghi, op2_aghi, &r1, &i2))
! pv_add_constant (&gpr[r1], i2);
!
! /* AR r1, r2 -- add register */
! else if (is_rr (insn, op_ar, &r1, &r2))
! pv_add (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* BASR r1, 0 --- branch and save
! Since r2 is zero, this saves the PC in r1, but doesn't branch. */
! else if (is_rr (insn, op_basr, &r1, &r2)
! && r2 == 0)
! pv_set_to_constant (&gpr[r1], next_pc);
!
! /* BRAS r1, i2 --- branch relative and save */
! else if (is_ri (insn, op1_bras, op2_bras, &r1, &i2))
! {
! pv_set_to_constant (&gpr[r1], next_pc);
! next_pc = pc + i2 * 2;
!
! /* We'd better not interpret any backward branches. We'll
! never terminate. */
! if (next_pc <= pc)
! break;
! }
!
! /* L r1, d2(x2, b2) --- load */
! else if (is_rx (insn, op_l, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
! struct prologue_value *stack;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! /* If it's a load from an in-line constant pool, then we can
! simulate that, under the assumption that the code isn't
! going to change between the time the processor actually
! executed it creating the current frame, and the time when
! we're analyzing the code to unwind past that frame. */
! if (addr.kind == pv_constant
! && start_pc <= addr.k
! && addr.k < next_pc)
! pv_set_to_constant (&gpr[r1],
! read_memory_integer (addr.k, 4));
!
! /* If it's definitely a reference to something on the stack,
! we can do that. */
! else if (s390_on_stack (&addr, 4, gpr, spill, &back_chain, &stack)
! == pv_definite_yes)
! gpr[r1] = *stack;
!
! /* Otherwise, we don't know the value. */
! else
! pv_set_to_unknown (&gpr[r1]);
! }
!
! /* LA r1, d2(x2, b2) --- load address */
! else if (is_rx (insn, op_la, &r1, &d2, &x2, &b2))
! compute_x_addr (&gpr[r1], gpr, d2, x2, b2);
!
! /* LARL r1, i2 --- load address relative long */
! else if (GDB_TARGET_IS_ESAME
! && is_ril (insn, op1_larl, op2_larl, &r1, &i2))
! pv_set_to_constant (&gpr[r1], pc + i2 * 2);
!
! /* LGR r1, r2 --- load from register */
! else if (GDB_TARGET_IS_ESAME
! && is_rre (insn, op_lgr, &r1, &r2))
! gpr[r1] = gpr[r2];
!
! /* LHI r1, i2 --- load halfword immediate */
! else if (is_ri (insn, op1_lhi, op2_lhi, &r1, &i2))
! pv_set_to_constant (&gpr[r1], i2);
!
! /* LGHI r1, i2 --- load halfword immediate --- 64-bit version */
! else if (is_ri (insn, op1_lghi, op2_lghi, &r1, &i2))
! pv_set_to_constant (&gpr[r1], i2);
!
! /* LR r1, r2 --- load from register */
! else if (is_rr (insn, op_lr, &r1, &r2))
! gpr[r1] = gpr[r2];
!
! /* NGR r1, r2 --- logical and --- 64-bit version */
! else if (GDB_TARGET_IS_ESAME
! && is_rre (insn, op_ngr, &r1, &r2))
! pv_logical_and (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* NR r1, r2 --- logical and */
! else if (is_rr (insn, op_nr, &r1, &r2))
! pv_logical_and (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* NGR r1, r2 --- logical and --- 64-bit version */
! else if (GDB_TARGET_IS_ESAME
! && is_rre (insn, op_ngr, &r1, &r2))
! pv_logical_and (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* NR r1, r2 --- logical and */
! else if (is_rr (insn, op_nr, &r1, &r2))
! pv_logical_and (&gpr[r1], &gpr[r1], &gpr[r2]);
!
! /* S r1, d2(x2, b2) --- subtract from memory */
! else if (is_rx (insn, op_s, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
! struct prologue_value value;
! struct prologue_value *stack;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! /* If it's a load from an in-line constant pool, then we can
! simulate that, under the assumption that the code isn't
! going to change between the time the processor actually
! executed it and the time when we're analyzing it. */
! if (addr.kind == pv_constant
! && start_pc <= addr.k
! && addr.k < pc)
! pv_set_to_constant (&value, read_memory_integer (addr.k, 4));
!
! /* If it's definitely a reference to something on the stack,
! we could do that. */
! else if (s390_on_stack (&addr, 4, gpr, spill, &back_chain, &stack)
! == pv_definite_yes)
! value = *stack;
!
! /* Otherwise, we don't know the value. */
! else
! pv_set_to_unknown (&value);
!
! pv_subtract (&gpr[r1], &gpr[r1], &value);
! }
!
! /* ST r1, d2(x2, b2) --- store */
! else if (is_rx (insn, op_st, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! /* The below really should be '4', not 'S390_GPR_SIZE'; this
! instruction always stores 32 bits, regardless of the full
! size of the GPR. */
! if (s390_store (&addr, 4, &gpr[r1], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* STD r1, d2(x2,b2) --- store floating-point register */
! else if (is_rx (insn, op_std, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! if (s390_store (&addr, 8, &fpr[r1], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* STG r1, d2(x2, b2) --- 64-bit store */
! else if (GDB_TARGET_IS_ESAME
! && is_rxe (insn, op1_stg, op2_stg, &r1, &d2, &x2, &b2))
! {
! struct prologue_value addr;
!
! compute_x_addr (&addr, gpr, d2, x2, b2);
!
! /* The below really should be '8', not 'S390_GPR_SIZE'; this
! instruction always stores 64 bits, regardless of the full
! size of the GPR. */
! if (s390_store (&addr, 8, &gpr[r1], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* STM r1, r3, d2(b2) --- store multiple */
! else if (is_rs (insn, op_stm, &r1, &r3, &d2, &b2))
! {
! int regnum;
! int offset;
! struct prologue_value addr;
!
! for (regnum = r1, offset = 0;
! regnum <= r3;
! regnum++, offset += 4)
! {
! compute_x_addr (&addr, gpr, d2 + offset, 0, b2);
!
! if (s390_store (&addr, 4, &gpr[regnum], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* If we left the loop early, we should stop interpreting
! altogether. */
! if (regnum <= r3)
! break;
! }
!
! /* STMG r1, r3, d2(b2) --- store multiple, 64-bit */
! else if (GDB_TARGET_IS_ESAME
! && is_rse (insn, op1_stmg, op2_stmg, &r1, &r3, &d2, &b2))
! {
! int regnum;
! int offset;
! struct prologue_value addr;
!
! for (regnum = r1, offset = 0;
! regnum <= r3;
! regnum++, offset += 8)
! {
! compute_x_addr (&addr, gpr, d2 + offset, 0, b2);
!
! if (s390_store (&addr, 8, &gpr[regnum], gpr, spill, &back_chain)
! == pv_maybe)
! /* If we can't be sure that it's *not* a store to
! something we're tracing, then we would have to mark all
! our memory as unknown --- after all, it *could* be a
! store to any of them --- so we might as well just stop
! interpreting. */
! break;
! }
!
! /* If we left the loop early, we should stop interpreting
! altogether. */
! if (regnum <= r3)
! break;
! }
!
! else
! /* An instruction we don't know how to simulate. The only
! safe thing to do would be to set every value we're tracking
! to 'unknown'. Instead, we'll be optimistic: we just stop
! interpreting, and assume that the machine state we've got
! now is good enough for unwinding the stack. */
! break;
!
! /* Record the address after the last instruction that changed
! the FP, SP, or backlink. Ignore instructions that changed
! them back to their original values --- those are probably
! restore instructions. (The back chain is never restored,
! just popped.) */
! {
! struct prologue_value *sp = &gpr[S390_SP_REGNUM - S390_GP0_REGNUM];
! struct prologue_value *fp = &gpr[S390_FRAME_REGNUM - S390_GP0_REGNUM];
!
! if ((! pv_is_identical (&pre_insn_sp, sp)
! && ! pv_is_register (sp, S390_SP_REGNUM, 0))
! || (! pv_is_identical (&pre_insn_fp, fp)
! && ! pv_is_register (fp, S390_FRAME_REGNUM, 0))
! || ! pv_is_identical (&pre_insn_back_chain, &back_chain))
! after_last_frame_setup_insn = next_pc;
! }
! }
!
! /* Okay, now gpr[], fpr[], spill[], and back_chain reflect the state
! of the machine as of the first instruction we couldn't interpret
! (hopefully the first non-prologue instruction). */
! {
! /* The size of the frame, or (CORE_ADDR) -1 if we couldn't figure
! that out. */
! CORE_ADDR frame_size = -1;
!
! /* The value the SP had upon entry to the function, or
! (CORE_ADDR) -1 if we can't figure that out. */
! CORE_ADDR original_sp = -1;
!
! /* Are we using S390_FRAME_REGNUM as a frame pointer register? */
! int using_frame_pointer = 0;
!
! /* If S390_FRAME_REGNUM is some constant offset from the SP, then
! that strongly suggests that we're going to use that as our
! frame pointer register, not the SP. */
! {
! struct prologue_value *fp = &gpr[S390_FRAME_REGNUM - S390_GP0_REGNUM];
!
! if (fp->kind == pv_register
! && fp->reg == S390_SP_REGNUM)
! using_frame_pointer = 1;
! }
!
! /* If we were given a frame_info structure, we may be able to use
! the frame's base address to figure out the actual value of the
! original SP. */
! if (fi && get_frame_base (fi))
! {
! int frame_base_regno;
! struct prologue_value *frame_base;
!
! /* The meaning of the frame base depends on whether the
! function uses a frame pointer register other than the SP or
! not (see s390_read_fp):
! - If the function does use a frame pointer register other
! than the SP, then the frame base is that register's
! value.
! - If the function doesn't use a frame pointer, then the
! frame base is the SP itself.
! We're duplicating some of the logic of s390_fp_regnum here,
! but we don't want to call that, because it would just do
! exactly the same analysis we've already done above. */
! if (using_frame_pointer)
! frame_base_regno = S390_FRAME_REGNUM;
! else
! frame_base_regno = S390_SP_REGNUM;
!
! frame_base = &gpr[frame_base_regno - S390_GP0_REGNUM];
!
! /* We know the frame base address; if the value of whatever
! register it came from is a constant offset from the
! original SP, then we can reconstruct the original SP just
! by subtracting off that constant. */
! if (frame_base->kind == pv_register
! && frame_base->reg == S390_SP_REGNUM)
! original_sp = get_frame_base (fi) - frame_base->k;
! }
!
! /* If the analysis said that the current SP value is the original
! value less some constant, then that constant is the frame size. */
! {
! struct prologue_value *sp = &gpr[S390_SP_REGNUM - S390_GP0_REGNUM];
!
! if (sp->kind == pv_register
! && sp->reg == S390_SP_REGNUM)
! frame_size = -sp->k;
! }
!
! /* If we knew other registers' current values, we could check if
! the analysis said any of those were related to the original SP
! value, too. But for now, we'll just punt. */
!
! /* If the caller passed in an 'extra info' structure, fill in the
! parts we can. */
! if (fextra_info)
! {
! if (init_extra_info || ! fextra_info->initialised)
! {
! s390_memset_extra_info (fextra_info);
! fextra_info->function_start = start_pc;
! fextra_info->initialised = 1;
! }
!
! if (frame_size != -1)
! {
! fextra_info->stack_bought = frame_size;
! }
!
! /* Assume everything was okay, and indicate otherwise when we
! find something amiss. */
! fextra_info->good_prologue = 1;
!
! if (using_frame_pointer)
! /* Actually, nobody cares about the exact PC, so any
! non-zero value will do here. */
! fextra_info->frame_pointer_saved_pc = 1;
!
! /* If we weren't able to find the size of the frame, or find
! the original sp based on actual current register values,
! then we're not going to be able to unwind this frame.
!
! (If we're just doing prologue analysis to set a breakpoint,
! then frame_size might be known, but original_sp unknown; if
! we're analyzing a real frame which uses alloca, then
! original_sp might be known (from the frame pointer
! register), but the frame size might be unknown.) */
! if (original_sp == -1 && frame_size == -1)
! fextra_info->good_prologue = 0;
!
! if (fextra_info->good_prologue)
! fextra_info->skip_prologue_function_start
! = after_last_frame_setup_insn;
! else
! /* If the prologue was too complex for us to make sense of,
! then perhaps it's better to just not skip anything at
! all. */
! fextra_info->skip_prologue_function_start = start_pc;
! }
!
! /* Indicate where registers were saved on the stack, if:
! - the caller seems to want to know,
! - the caller provided an actual SP, and
! - the analysis gave us enough information to actually figure it
! out. */
! if (fi
! && get_frame_saved_regs (fi)
! && original_sp != -1)
! {
! int slot_num;
! CORE_ADDR slot_addr;
! CORE_ADDR *saved_regs = get_frame_saved_regs (fi);
!
! /* Scan the spill array; if a spill slot says it holds the
! original value of some register, then record that slot's
! address as the place that register was saved.
!
! Just for kicks, note that, even if registers aren't saved
! in their officially-sanctioned slots, this will still work
! --- we know what really got put where. */
!
! /* First, the slots for r2 -- r15. */
! for (slot_num = 0, slot_addr = original_sp + 2 * S390_GPR_SIZE;
! slot_num < 14;
! slot_num++, slot_addr += S390_GPR_SIZE)
! {
! struct prologue_value *slot = &spill[slot_num];
!
! if (slot->kind == pv_register
! && slot->k == 0)
! saved_regs[slot->reg] = slot_addr;
! }
!
! /* Then, the slots for f0, f2, f4, and f6. They're a
! different size. */
! for (slot_num = 14, slot_addr = original_sp + 16 * S390_GPR_SIZE;
! slot_num < S390_NUM_SPILL_SLOTS;
! slot_num++, slot_addr += S390_FPR_SIZE)
! {
! struct prologue_value *slot = &spill[slot_num];
!
! if (slot->kind == pv_register
! && slot->k == 0)
! saved_regs[slot->reg] = slot_addr;
! }
!
! /* The stack pointer's element of saved_regs[] is special. */
! saved_regs[S390_SP_REGNUM] = original_sp;
! }
! }
!
! return result;
}
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2003-05-04 22:11 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-04-18 21:53 [just for the record]: new prologue analyzer for S/390 Jim Blandy
2003-04-21 16:31 ` Andrew Cagney
2003-04-21 16:38 ` Daniel Jacobowitz
2003-04-21 21:11 ` Andrew Cagney
2003-04-21 21:54 ` Jim Blandy
2003-05-04 22:11 ` Jim Blandy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox