public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* How to generate a call inst. sequence?
@ 2022-01-19  4:53 Andras Tantos
  2022-01-19 10:45 ` Richard Sandiford
  0 siblings, 1 reply; 3+ messages in thread
From: Andras Tantos @ 2022-01-19  4:53 UTC (permalink / raw)
  To: gcc

All,

I'm working on porting GCC to a processor architecture that doesn't have 
a (HW) stack nor a call instruction. This means that for calls, I need 
to generate the following instruction sequence:

     // move stack-pointer:
     $sp <- $sp-4
     // load return address:
     $r3 <- return_label
     // store return address on stack:
     mem[$sp] <- $r3
     // jump to callee:
     $pc <- <address_of_function>
   return_label:

Now, I can do all of that as a multi-instruction string sequence in my 
.md file (which is what I'm doing right now), but there are two problems 
with that approach. First, it hard-codes the temp register ($r3 above) 
and requires me to reserve it even though it could be used between calls 
by the register allocator. Second this approach (I think at least) 
prevents any passes from merging stack-frame preparation for the call 
arguments, such as eliminating the stack-pointer update above.

I thought I could circumvent these problems by emitting a piece of RTL 
in the 'call' pattern:

   (define_expand "call"
     [(call
       (match_operand:QI 0 "memory_operand" "")
       (match_operand 1 "" "")
     )]
     ""
   {
     brew_expand_call(Pmode, operands);
   })

where brew_expand_call is:

   void brew_expand_call(machine_mode mode, rtx *operands)
   {
     gcc_assert (MEM_P(operands[0]));

     rtx_code_label *label = gen_label_rtx();
     rtx label_ref = gen_rtx_LABEL_REF(SImode, label);
     rtx temp_reg = gen_reg_rtx(mode);

     // $sp <- $sp - 4
     emit_insn(gen_subsi3(
       stack_pointer_rtx,
       stack_pointer_rtx,
       GEN_INT(4)
     ));
     // $r3 <- <ret_label>
     emit_insn(gen_move_insn(
       temp_reg,
       label_ref
     ));
     // mem[$sp] <- $r3
     emit_insn(gen_move_insn(
       gen_rtx_MEM(Pmode, stack_pointer_rtx),
       temp_reg
     ));
     emit_jump_insn(gen_jump(operands[0]));
     emit_label(label);
   }

If I try to compile the following test:

   void x(void)
   {
   }

   int main(void)
   {
     x();
     return 0;
   }

I get an assert:

   during RTL pass: expand
   dump file: call.c.252r.expand
   call.c: In function ‘main’:
   call.c:9:1: internal compiler error: in as_a, at is-a.h:242
       9 | }
         | ^
   0x6999b7 rtx_insn* as_a<rtx_insn*, rtx_def>(rtx_def*)
       ../../brew-gcc/gcc/is-a.h:242
   0x6999b7 rtx_sequence::insn(int) const
       ../../brew-gcc/gcc/rtl.h:1439
   0x6999b7 mark_jump_label_1
       ../../brew-gcc/gcc/jump.cc:1077
   0xcfc31f mark_jump_label_1
       ../../brew-gcc/gcc/jump.cc:1171
   0xcfc73d mark_all_labels
       ../../brew-gcc/gcc/jump.cc:332
   0xcfc73d rebuild_jump_labels_1
       ../../brew-gcc/gcc/jump.cc:74
   0x9e8e62 execute
       ../../brew-gcc/gcc/cfgexpand.cc:6845

The reference dump file:

   ;; Function x (x, funcdef_no=0, decl_uid=1383, cgraph_uid=1, 
symbol_order=0)


   ;; Generating RTL for gimple basic block 2


   try_optimize_cfg iteration 1

   Merging block 3 into block 2...
   Merged blocks 2 and 3.
   Merged 2 and 3 without moving.
   Merging block 4 into block 2...
   Merged blocks 2 and 4.
   Merged 2 and 4 without moving.


   try_optimize_cfg iteration 2



   ;;
   ;; Full RTL generated for this function:
   ;;
   (note 1 0 3 NOTE_INSN_DELETED)
   (note 3 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
   (note 2 3 0 2 NOTE_INSN_FUNCTION_BEG)

   ;; Function main (main, funcdef_no=1, decl_uid=1386, cgraph_uid=2, 
symbol_order=1)


   ;; Generating RTL for gimple basic block 2

   ;; Generating RTL for gimple basic block 3



   EMERGENCY DUMP:

   int main ()
   {
   (note 3 1 2 4 [bb 4] NOTE_INSN_BASIC_BLOCK)
   (note 2 3 4 4 NOTE_INSN_FUNCTION_BEG)

   (note 4 2 5 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
   (insn 5 4 6 2 (set (reg/f:SI 1 $sp)
           (minus:SI (reg/f:SI 1 $sp)
               (const_int 4 [0x4]))) "call.c":7:5 -1
       (nil))
   (insn 6 5 7 2 (set (reg:SI 25)
           (label_ref:SI 9)) "call.c":7:5 -1
       (insn_list:REG_LABEL_OPERAND 9 (nil)))
   (insn 7 6 8 2 (set (mem:SI (reg/f:SI 1 $sp) [0  S4 A32])
           (reg:SI 25)) "call.c":7:5 -1
       (nil))
   (jump_insn 8 7 9 2 (set (pc)
           (label_ref (mem:QI (symbol_ref:SI ("x") [flags 0x3] 
<function_decl 0x7f594efa9200 x>) [0 x S1 A8]))) "call.c":7:5 -1
       (nil))
   (code_label 9 8 10 2 3 (nil) [1 uses])
   (call_insn 10 9 11 2 (call (mem:QI (symbol_ref:SI ("x") [flags 0x3]  
<function_decl 0x7f594efa9200 x>) [0 x S1 A8])
           (const_int 16 [0x10])) "call.c":7:5 -1
       (nil)
       (nil))
   (insn 11 10 12 2 (set (reg:SI 23 [ _3 ])
           (const_int 0 [0])) "call.c":8:12 -1
       (nil))

   (code_label 12 11 13 3 4 (nil) [0 uses])
   (note 13 12 14 3 [bb 3] NOTE_INSN_BASIC_BLOCK)
   (insn 14 13 15 3 (set (reg:SI 24 [ <retval> ])
           (reg:SI 23 [ _3 ])) "call.c":9:1 -1
       (nil))
   (jump_insn 15 14 16 3 (set (pc)
           (label_ref 17)) "call.c":9:1 -1
       (nil))

   (code_label 17 16 20 5 2 (nil) [0 uses])
   (note 20 17 18 5 [bb 5] NOTE_INSN_BASIC_BLOCK)
   (insn 18 20 19 5 (set (reg/i:SI 4 $r4)
           (reg:SI 24 [ <retval> ])) "call.c":9:1 -1
       (nil))
   (insn 19 18 0 5 (use (reg/i:SI 4 $r4)) "call.c":9:1 -1
       (nil))

   }

As a test to narrow the problem down, I removed the 'emit_jump_insn' 
call above. That generated an assembly (thus proving the theory that the 
assert has something to do with that), but then the assembly doesn't 
contain my label, only a reference to it; which of course later on would 
result in a linker error.

So, what am I doing wrong and how can I achieve what I want?

Thank you all wise sages!
Andras

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-01-20 23:48 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-19  4:53 How to generate a call inst. sequence? Andras Tantos
2022-01-19 10:45 ` Richard Sandiford
2022-01-20 23:47   ` Andras Tantos

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).