📄 expr.c
字号:
if (partial != 0) xinner = change_address (xinner, BLKmode, plus_constant (XEXP (xinner, 0), used));/* If the partial register-part of the arg counts in its stack size, skip the part of stack space corresponding to the registers. Otherwise, start copying to the beginning of the stack space, by setting SKIP to 0. */#ifndef FIRST_PARM_CALLER_OFFSET skip = 0;#else skip = used;#endif#ifdef PUSH_ROUNDING /* Do it with several push insns if that doesn't take lots of insns and if there is no difficulty with push insns that skip bytes on the stack for alignment purposes. */ if (args_addr == 0 && GET_CODE (size) == CONST_INT && args_addr == 0 && skip == 0 && (move_by_pieces_ninsns ((unsigned) INTVAL (size) - used, align) < MOVE_RATIO) && PUSH_ROUNDING (INTVAL (size)) == INTVAL (size)) move_by_pieces (gen_rtx (MEM, BLKmode, gen_push_operand ()), xinner, INTVAL (size) - used, align); else#endif /* PUSH_ROUNDING */ { /* Otherwise make space on the stack and copy the data to the address of that space. */ /* Deduct words put into registers from the size we must copy. */ if (partial != 0) { if (GET_CODE (size) == CONST_INT) size = gen_rtx (CONST_INT, VOIDmode, INTVAL (size) - used); else size = expand_binop (GET_MODE (size), sub_optab, size, gen_rtx (CONST_INT, VOIDmode, used), 0, 0, OPTAB_LIB_WIDEN); } /* Get the address of the stack space. */ if (! args_addr) { temp = push_block (size, extra); extra = 0; } else if (GET_CODE (args_so_far) == CONST_INT) temp = memory_address (BLKmode, plus_constant (args_addr, skip + INTVAL (args_so_far))); else temp = memory_address (BLKmode, plus_constant (gen_rtx (PLUS, Pmode, args_addr, args_so_far), skip)); /* TEMP is the address of the block. Copy the data there. */ if (GET_CODE (size) == CONST_INT && (move_by_pieces_ninsns ((unsigned) INTVAL (size), align) < MOVE_RATIO)) { move_by_pieces (gen_rtx (MEM, BLKmode, temp), xinner, INTVAL (size), align); goto ret; } /* Try the most limited insn first, because there's no point including more than one in the machine description unless the more limited one has some advantage. */#ifdef HAVE_movstrqi if (HAVE_movstrqi && GET_CODE (size) == CONST_INT && ((unsigned) INTVAL (size) < (1 << (GET_MODE_BITSIZE (QImode) - 1)))) { emit_insn (gen_movstrqi (gen_rtx (MEM, BLKmode, temp), xinner, size, gen_rtx (CONST_INT, VOIDmode, align))); goto ret; }#endif#ifdef HAVE_movstrhi if (HAVE_movstrhi && GET_CODE (size) == CONST_INT && ((unsigned) INTVAL (size) < (1 << (GET_MODE_BITSIZE (HImode) - 1)))) { emit_insn (gen_movstrhi (gen_rtx (MEM, BLKmode, temp), xinner, size, gen_rtx (CONST_INT, VOIDmode, align))); goto ret; }#endif#ifdef HAVE_movstrsi if (HAVE_movstrsi) { emit_insn (gen_movstrsi (gen_rtx (MEM, BLKmode, temp), xinner, size, gen_rtx (CONST_INT, VOIDmode, align))); goto ret; }#endif if (reg_mentioned_p (stack_pointer_rtx, temp)) { /* Now that emit_library_call does force_operand before pushing anything, preadjustment does not work. */ temp = copy_to_reg (temp);#if 0 /* Correct TEMP so it holds what will be a description of the address to copy to, valid after one arg is pushed. */ int xsize = GET_MODE_SIZE (Pmode);#ifdef PUSH_ROUNDING xsize = PUSH_ROUNDING (xsize);#endif xsize = ((xsize + PARM_BOUNDARY / BITS_PER_UNIT - 1) / (PARM_BOUNDARY / BITS_PER_UNIT) * (PARM_BOUNDARY / BITS_PER_UNIT));#ifdef TARGET_MEM_FUNCTIONS /* If we are calling bcopy, we push one arg before TEMP. If calling memcpy, we push two. */ xsize *= 2;#endif#ifdef STACK_GROWS_DOWNWARD temp = plus_constant (temp, xsize);#else temp = plus_constant (temp, -xsize);#endif /* not STACK_GROWS_DOWNWARD */#endif /* 0 */ } /* Make inhibit_defer_pop nonzero around the library call to force it to pop the bcopy-arguments right away. */ NO_DEFER_POP;#ifdef TARGET_MEM_FUNCTIONS emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, VOIDmode, 3, temp, Pmode, XEXP (xinner, 0), Pmode, size, Pmode);#else emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, VOIDmode, 3, XEXP (xinner, 0), Pmode, temp, Pmode, size, Pmode);#endif OK_DEFER_POP; } } else if (partial > 0) { /* Scalar partly in registers. */ int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD; int i; int not_stack; /* # words of start of argument that we must make space for but need not store. */ int offset = partial % (PARM_BOUNDARY / BITS_PER_WORD); int args_offset = INTVAL (args_so_far); int skip; /* If we make space by pushing it, we might as well push the real data. Otherwise, we can leave OFFSET nonzero and leave the space uninitialized. */ if (args_addr == 0) offset = 0; /* Now NOT_STACK gets the number of words that we don't need to allocate on the stack. */ not_stack = partial - offset;/* If the partial register-part of the arg counts in its stack size, skip the part of stack space corresponding to the registers. Otherwise, start copying to the beginning of the stack space, by setting SKIP to 0. */#ifndef FIRST_PARM_CALLER_OFFSET skip = 0;#else skip = not_stack;#endif if (GET_CODE (x) == CONST_DOUBLE && x != dconst0_rtx) x = force_const_double_mem (x); /* Loop over all the words allocated on the stack for this arg. */ /* We can do it by words, because any scalar bigger than a word has a size a multiple of a word. */#ifndef PUSH_ARGS_REVERSED for (i = not_stack; i < size; i++)#else for (i = size - 1; i >= not_stack; i--)#endif if (i >= not_stack + offset) { rtx wd; rtx addr; /* Get the next word of the value in WD. */ if (GET_CODE (x) == MEM) { rtx addr = memory_address (SImode, plus_constant (XEXP (x, 0), i * UNITS_PER_WORD)); /* Copy to a reg, since machine may lack memory-to-memory move insns. */ wd = copy_to_reg (gen_rtx (MEM, SImode, addr)); } else if (GET_CODE (x) == REG) wd = gen_rtx (SUBREG, SImode, x, i); else if (x == dconst0_rtx || x == const0_rtx) wd = const0_rtx; else abort (); emit_push_insn (wd, SImode, 0, align, 0, 0, 0, args_addr, gen_rtx (CONST_INT, VOIDmode, args_offset + (i - not_stack + skip) * UNITS_PER_WORD)); } } else { rtx addr;#ifdef PUSH_ROUNDING if (args_addr == 0) addr = gen_push_operand (); else#endif if (GET_CODE (args_so_far) == CONST_INT) addr = memory_address (mode, plus_constant (args_addr, INTVAL (args_so_far))); else addr = memory_address (mode, gen_rtx (PLUS, Pmode, args_addr, args_so_far)); emit_move_insn (gen_rtx (MEM, mode, addr), x); } ret: /* If part should go in registers, copy that part into the appropriate registers. Do this now, at the end, since mem-to-mem copies above may do function calls. */ if (partial > 0) move_block_to_reg (REGNO (reg), x, partial, align * BITS_PER_UNIT); if (extra) anti_adjust_stack (gen_rtx (CONST_INT, VOIDmode, extra));}/* Output a library call to function FUN (a SYMBOL_REF rtx) (emitting the queue unless NO_QUEUE is nonzero), for a value of mode OUTMODE, with NARGS different arguments, passed as alternating rtx values and machine_modes to convert them to. The rtx values should have been passed through protect_from_queue already. */voidemit_library_call (va_alist) va_dcl{ register va_list p; register int args_size = 0; register int argnum; enum machine_mode outmode; int nargs; rtx fun; rtx orgfun; int inc; int count; rtx *regvec; rtx argblock = 0; CUMULATIVE_ARGS args_so_far; struct arg { rtx value; enum machine_mode mode; rtx reg; int partial; }; struct arg *argvec; int old_inhibit_defer_pop = inhibit_defer_pop; int stack_padding = 0; int no_queue = 0; rtx use_insns; va_start (p); orgfun = fun = va_arg (p, rtx); no_queue = va_arg (p, int); outmode = va_arg (p, enum machine_mode); nargs = va_arg (p, int); regvec = (rtx *) alloca (nargs * sizeof (rtx)); /* Copy all the libcall-arguments out of the varargs data and into a vector ARGVEC. */ argvec = (struct arg *) alloca (nargs * sizeof (struct arg)); INIT_CUMULATIVE_ARGS (args_so_far, (tree)0); for (count = 0; count < nargs; count++) { rtx val = va_arg (p, rtx); enum machine_mode mode = va_arg (p, enum machine_mode); int arg_size; argvec[count].value = val; /* Convert the arg value to the mode the library wants. Also make sure it is a reasonable operand for a move or push insn. */ /* ??? It is wrong to do it here; must do it earlier where we know the signedness of the arg. */#ifdef GNULIB_NEEDS_DOUBLE if (GNULIB_NEEDS_DOUBLE && mode == SFmode) mode = DFmode;#endif if (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode) { val = gen_reg_rtx (mode); convert_move (val, argvec[count].value, 0); } else if (GET_CODE (val) != REG && GET_CODE (val) != MEM && ! ((CONSTANT_P (val) || GET_CODE (val) == CONST_DOUBLE) && LEGITIMATE_CONSTANT_P (val))) val = force_operand (val, 0); argvec[count].value = val; argvec[count].mode = mode; regvec[count] = FUNCTION_ARG (args_so_far, mode, (tree)0, 1);#ifdef FUNCTION_ARG_PARTIAL_NREGS argvec[count].partial = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, (tree)0, 1);#else argvec[count].partial = 0;#endif FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1); } va_end (p); /* If we have no actual push instructions, make space for all the args right now. */#ifndef PUSH_ROUNDING for (count = 0; count < nargs; count++) { register enum machine_mode mode = argvec[count].mode; register rtx reg = regvec[count]; register int partial = argvec[count].partial; if (reg == 0 || partial != 0) args_size += GET_MODE_SIZE (mode); if (partial != 0) args_size -= partial * GET_MODE_SIZE (SImode); } if (args_size != 0) {#ifdef STACK_ARGS_ADJUST struct args_size size; size.constant = args_size; size.var = 0; STACK_ARGS_ADJUST (size); args_size = size.constant;#endif argblock = push_block (round_push (gen_rtx (CONST_INT, VOIDmode, args_size)), 0); }#endif /* no PUSH_ROUNDING */#ifdef PUSH_ARGS_REVERSED inc = -1; argnum = nargs - 1;#else inc = 1; argnum = 0;#endif args_size = stack_padding; for (count = 0; count < nargs; count++, argnum += inc) { register enum machine_mode mode = argvec[argnum].mode; register rtx val = argvec[argnum].value; rtx reg = regvec[argnum]; int partial = argvec[argnum].partial; int arg_size; if (reg != 0 && partial == 0) emit_move_insn (reg, val); else emit_push_insn (val, mode, 0, 0, partial, reg, 0, argblock, gen_rtx (CONST_INT, VOIDmode, args_size)); /* Compute size of stack space used by this argument. */ if (reg == 0 || partial != 0) arg_size = GET_MODE_SIZE (mode); else arg_size = 0; if (partial != 0) arg_size -= ((partial * UNITS_PER_WORD) / (PARM_BOUNDARY / BITS_PER_UNIT) * (PARM_BOUNDARY / BITS_PER_UNIT)); args_size += arg_size; NO_DEFER_POP; } /* For version 1.37, try deleting this entirely. */ if (! no_queue) emit_queue (); fun = prepare_call_address (fun, 0); /* Any regs containing parms remain in use through the call. */ start_sequence (); for (count = 0; count < nargs; count++) if (regvec[count] != 0) emit_insn (gen_rtx (USE, VOIDmode, regvec[count])); use_insns = gen_sequence (); end_sequence ();#ifdef STACK_BOUNDARY args_size = (args_size + STACK_BYTES - 1) / STACK_BYTES * STACK_BYTES;#endif /* Don't allow popping to be deferred, since then cse'ing of library calls could delete a call and leave the pop. */ NO_DEFER_POP; emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size, FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), outmode != VOIDmode ? hard_libcall_value (outmode) : 0, old_inhibit_defer_pop + 1, use_insns); OK_DEFER_POP;}/* Expand an assignment that stores the value of FROM into TO. If WANT_VALUE is nonzero, return an rtx for the value of TO. (This may contain a QUEUED rtx.) Otherwise, the returned value is not meaningful. SUGGEST_REG is no longer actually used. It used to mean, copy the value through a register and return that register, if that is possible. But now we do this if WANT_VALUE. If the value stored is a constant, we return the constant. */rtxexpand_assignment (to, from, want_value, suggest_reg) tree to, from; int want_value; int suggest_reg;{ register rtx to_rtx = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -