From: Richard Sandiford <richard.sandiford@arm.com>
To: Andras Tantos <andras@tantosonline.com>
Cc: gcc@gcc.gnu.org
Subject: Re: How to generate a call inst. sequence?
Date: Wed, 19 Jan 2022 10:45:28 +0000 [thread overview]
Message-ID: <mptbl08ovg7.fsf@arm.com> (raw)
In-Reply-To: <e26871d2-11f5-585b-36ad-e8a49d202ac5@tantosonline.com> (Andras Tantos's message of "Tue, 18 Jan 2022 20:53:43 -0800")
Andras Tantos <andras@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?
next prev parent reply other threads:[~2022-01-19 10:45 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-01-19 4:53 Andras Tantos
2022-01-19 10:45 ` Richard Sandiford [this message]
2022-01-20 23:47 ` Andras Tantos
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=mptbl08ovg7.fsf@arm.com \
--to=richard.sandiford@arm.com \
--cc=andras@tantosonline.com \
--cc=gcc@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).