"!HALF_PIC_P ()"
"call %P1")
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand:QI 0 "indirect_operand" "")
+ (const_int 0))
+ (match_operand:BLK 1 "memory_operand" "")
+ (match_operand 2 "" "")])]
+ ""
+ "
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[0], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[0], 0) = force_reg (Pmode, addr);
+
+ operands[1] = change_address (operands[1], DImode, XEXP (operands[1], 0));
+}")
+
+(define_insn ""
+ [(call (match_operand:QI 0 "indirect_operand" "m")
+ (const_int 0))
+ (match_operand:DI 1 "memory_operand" "o")
+ (match_operand 2 "" "")]
+ ""
+ "*
+{
+ rtx addr = operands[1];
+
+ if (GET_CODE (operands[0]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+ {
+ operands[0] = XEXP (operands[0], 0);
+ output_asm_insn (AS1 (call,%*%0), operands);
+ }
+ else
+ output_asm_insn (AS1 (call,%P0), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 0);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 1);
+ operands[1] = adj_offsettable_operand (addr, 4);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[1] = adj_offsettable_operand (addr, 8);
+ return AS1 (fnsave,%1);
+}")
+
+(define_insn ""
+ [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
+ (const_int 0))
+ (match_operand:DI 1 "memory_operand" "o")
+ (match_operand 2 "" "")]
+ "!HALF_PIC_P ()"
+ "*
+{
+ rtx addr = operands[1];
+
+ output_asm_insn (AS1 (call,%P0));
+
+ operands[2] = gen_rtx (REG, SImode, 0);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 1);
+ operands[1] = adj_offsettable_operand (addr, 4);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[1] = adj_offsettable_operand (addr, 8);
+ return AS1 (fnsave,%1);
+}")
+
+;; We use fnsave and frstor to save and restore the floating point result.
+;; These are expensive instructions and require a large space to save the
+;; FPU state. An more complicated alternative is to use fnstenv to store
+;; the FPU environment and test whether the stack top is valid. Store the
+;; result of the test, and if it is valid, pop and save the value. The
+;; untyped_return would check the test and optionally push the saved value.
+
+(define_expand "untyped_return"
+ [(match_operand:BLK 0 "memory_operand" "")
+ (match_operand 1 "" "")]
+ ""
+ "
+{
+ rtx valreg1 = gen_rtx (REG, SImode, 0);
+ rtx valreg2 = gen_rtx (REG, SImode, 1);
+ rtx result = operands[0];
+
+ /* Restore the FPU state. */
+ emit_insn (gen_update_return (change_address (result, SImode,
+ plus_constant (XEXP (result, 0),
+ 8))));
+
+ /* Reload the function value registers. */
+ emit_move_insn (valreg1, change_address (result, SImode, XEXP (result, 0)));
+ emit_move_insn (valreg2,
+ change_address (result, SImode,
+ plus_constant (XEXP (result, 0), 4)));
+
+ /* Put USE insns before the return. */
+ emit_insn (gen_rtx (USE, VOIDmode, valreg1));
+ emit_insn (gen_rtx (USE, VOIDmode, valreg2));
+
+ /* Construct the return. */
+ expand_null_return ();
+
+ DONE;
+}")
+
+(define_insn "update_return"
+ [(unspec:SI [(match_operand:SI 0 "memory_operand" "m")] 0)]
+ ""
+ "frstor %0")
+
;; Insn emitted into the body of a function to return from a function.
;; This is only done if the function's epilogue is known to be simple.
;; See comments for simple_386_epilogue in i386.c.
return \"call %a1,%2%#\";
}"
[(set_attr "type" "call")])
+
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand:SI 0 "call_operand" "")
+ (const_int 0))
+ (match_operand:BLK 1 "memory_operand" "")
+ (match_operand 2 "" "")
+ (clobber (reg:SI 15))])]
+ ""
+ "
+{
+ operands[1] = change_address (operands[1], DImode, XEXP (operands[1], 0));
+}")
+
+;; Make a call followed by two nops in case the function being called
+;; returns a structure value and expects to skip an unimp instruction.
+
+(define_insn ""
+ [(call (mem:SI (match_operand:SI 0 "call_operand_address" "rS"))
+ (const_int 0))
+ (match_operand:DI 1 "memory_operand" "o")
+ (match_operand 2 "" "")
+ (clobber (reg:SI 15))]
+ ""
+ "*
+{
+ operands[2] = adj_offsettable_operand (operands[1], 8);
+ return \"call %a0,0\;nop\;nop\;std %%o0,%1\;st %%f0,%2\";
+}"
+ [(set_attr "type" "multi")])
+
+;; Prepare to return any type including a structure value.
+
+(define_expand "untyped_return"
+ [(match_operand:BLK 0 "memory_operand" "")
+ (match_operand 1 "" "")]
+ ""
+ "
+{
+ rtx valreg1 = gen_rtx (REG, DImode, 24);
+ rtx valreg2 = gen_rtx (REG, DFmode, 32);
+ rtx result = operands[0];
+ rtx rtnreg = gen_rtx (REG, SImode, (leaf_function ? 15 : 31));
+ rtx value = gen_reg_rtx (SImode);
+
+ /* Fetch the instruction where we will return to and see if it's an unimp
+ instruction (the most significant 10 bits will be zero). If so,
+ update the return address to skip the unimp instruction. */
+ emit_move_insn (value,
+ gen_rtx (MEM, SImode, plus_constant (rtnreg, 8)));
+ emit_insn (gen_lshrsi3 (value, value, GEN_INT (22)));
+ emit_insn (gen_update_return (rtnreg, value));
+
+ /* Reload the function value registers. */
+ emit_move_insn (valreg1, change_address (result, DImode, XEXP (result, 0)));
+ emit_move_insn (valreg2,
+ change_address (result, DFmode,
+ plus_constant (XEXP (result, 0), 8)));
+
+ /* Put USE insns before the return. */
+ emit_insn (gen_rtx (USE, VOIDmode, valreg1));
+ emit_insn (gen_rtx (USE, VOIDmode, valreg2));
+
+ /* Construct the return. */
+ expand_null_return ();
+
+ DONE;
+}")
+
+;; This is a bit of a hack. We're incrementing a fixed register (%i7),
+;; and parts of the compiler don't want to believe that the add is needed.
+
+(define_insn "update_return"
+ [(unspec:SI [(match_operand:SI 0 "register_operand" "r")
+ (match_operand:SI 1 "register_operand" "r")] 0)]
+ ""
+ "cmp %1,0\;be,a .+8\;add %0,4,%0"
+ [(set_attr "type" "multi")])
\f
(define_insn "return"
[(return)]