Andras Tantos <and...@tantosonline.com> writes:
> 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>

Even though this is internally a jump, it still needs to be represented
as a (call …) rtx in rtl, and emitted using emit_call_insn.

In other words, the "call" expander must always emit a call_insn
of some kind.  (But it can emit other instructions too, such as the
ones you describe above.)

Richard

>    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?

Reply via email to