From 93840d98922db9fc838ada24fabf49d016285007 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 1 Feb 2022 21:46:28 +0900 Subject: [PATCH] [qfcc] Handle Ruamoko's call return destination Since the call instruction in the Ruamoko ISA specifies the destination of the return value of the called function, it is much like any expression type instruction in that the def referenced by its c operand is both defined and killed by the instruction. However, unlike other instructions, it really has many pseudo-operands: the arguments placed on the stack. The problem is that when one of the arguments is also the destination of the return value, the dags code wants to use the stack argument as it was the last use of the real argument. Thus, instead of using the value of the child node for the result, use the value label attached to the call node (there should be only one such label). This fixes iterfunc, typedef, zerolinker and vkgen when optimizing. Now all but the double tests and return postop tests pass (and the retun postop test is not related to the Ruamoko ISA, so fails either way). --- tools/qfcc/source/dags.c | 44 +++++++++++++++++++++++++++++++++++++++- tools/qfcc/source/flow.c | 3 +++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/tools/qfcc/source/dags.c b/tools/qfcc/source/dags.c index 7a52a4f6b..e55688ff4 100644 --- a/tools/qfcc/source/dags.c +++ b/tools/qfcc/source/dags.c @@ -1135,6 +1135,43 @@ generate_memsetps (dag_t *dag, sblock_t *block, dagnode_t *dagnode) return dst; } +static operand_t * +generate_call (dag_t *dag, sblock_t *block, dagnode_t *dagnode) +{ + set_iter_t *var_iter; + daglabel_t *var = 0; + operand_t *operands[3] = {0, 0, 0}; + statement_t *st; + operand_t *dst; + + operands[0] = make_operand (dag, block, dagnode, 0); + if (dagnode->children[1]) { + operands[1] = make_operand (dag, block, dagnode, 1); + } + dst = operands[0]; + for (var_iter = set_first (dagnode->identifiers); var_iter; + var_iter = set_next (var_iter)) { + if (var) { + internal_error (var->expr, "more than one return value for call"); + } + var = dag->labels[var_iter->element]; + operands[2] = var->op; + dst = operands[2]; + st = build_statement ("call", operands, var->expr); + sblock_add_statement (block, st); + } + if (var_iter) { + set_del_iter (var_iter); + } + if (!var) { + // void call or return value ignored, still have to call + operands[2] = make_operand (dag, block, dagnode, 2); + st = build_statement ("call", operands, dagnode->label->expr); + sblock_add_statement (block, st); + } + return dst; +} + static operand_t * generate_assignments (dag_t *dag, sblock_t *block, operand_t *src, set_iter_t *var_iter, type_t *type) @@ -1222,8 +1259,13 @@ dag_gencode (dag_t *dag, sblock_t *block, dagnode_t *dagnode) case st_ptrmemset: dst = generate_memsetps (dag, block, dagnode); break; - case st_state: case st_func: + if (!strcmp (dagnode->label->opcode, "call")) { + dst = generate_call (dag, block, dagnode); + break; + } + // fallthrough + case st_state: for (i = 0; i < 3; i++) if (dagnode->children[i]) operands[i] = make_operand (dag, block, dagnode, i); diff --git a/tools/qfcc/source/flow.c b/tools/qfcc/source/flow.c index 4b501c027..4592ae8b6 100644 --- a/tools/qfcc/source/flow.c +++ b/tools/qfcc/source/flow.c @@ -1290,6 +1290,9 @@ flow_analyze_statement (statement_t *s, set_t *use, set_t *def, set_t *kill, flow_add_op_var (def, s->opc, 0); // don't want old argument processing calln = -1; + if (operands && s->opc->op_type != op_value) { + operands[0] = s->opc; + } } else if (strncmp (s->opcode, "call", 4) == 0) { start = 0; calln = s->opcode[5] - '0';