* 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
* Re: How to generate a call inst. sequence?
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
0 siblings, 1 reply; 3+ messages in thread
From: Richard Sandiford @ 2022-01-19 10:45 UTC (permalink / raw)
To: Andras Tantos; +Cc: gcc
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?
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: How to generate a call inst. sequence?
2022-01-19 10:45 ` Richard Sandiford
@ 2022-01-20 23:47 ` Andras Tantos
0 siblings, 0 replies; 3+ messages in thread
From: Andras Tantos @ 2022-01-20 23:47 UTC (permalink / raw)
To: Richard Sandiford; +Cc: gcc
On Wed, 2022-01-19 at 10:45 +0000, Richard Sandiford wrote:
> 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
>
Richard,
Thanks for the reply. While what you're saying makes sense, it didn't
solve my problems. The symptoms changed, but didn't completely go away.
At the same time, I realized that - in this architecture - link-
register-style calls are more efficient anyway, so I've changed my call
implementation to that configuration. That got rid of the need for
sloving this particular problem.
So, just to document for people who might be looking at this thread in
the future: this wasn't the complete answer to my problem, but I took a
different route which removed the whole problem class.
Thanks again,
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).