mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 15:22:04 +00:00
[qfcc] Implement bounced return pointer calls
This is achieved by marking a void function with the void_return attribute and then calling that function in an @return expression. @return can be used only inside a void function and only with void functions marked with the void_return attribute. As this is intended for Objective-QC message forwarding, it is deliberately "difficult" to use as returning a larger than expected value is unlikely to end well for the calling function. However, as a convenience, "@return nil" is allowed (in a void function). It always returns an integer (which, of course,can be interpreted as a pointer). This is safe because if the return value is ignored, it will go into the progs return buffer, and if it is not ignored, it is the smallest value that can be returned.
This commit is contained in:
parent
084c2ccb1f
commit
1ce026d168
6 changed files with 60 additions and 4 deletions
|
@ -236,6 +236,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
struct expr_s *ret_val;
|
||||
int at_return; ///< return void_return call through void
|
||||
} ex_return_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -244,7 +245,7 @@ typedef struct {
|
|||
} ex_adjstk_t;
|
||||
|
||||
typedef struct {
|
||||
short mode; ///< currently must be 0
|
||||
short mode;
|
||||
short reg; ///< base register to load
|
||||
struct expr_s *with; ///< value to load
|
||||
} ex_with_t;
|
||||
|
@ -791,6 +792,7 @@ expr_t *goto_expr (expr_t *label);
|
|||
expr_t *jump_table_expr (expr_t *table, expr_t *index);
|
||||
expr_t *call_expr (expr_t *func, expr_t *args, struct type_s *ret_type);
|
||||
expr_t *return_expr (struct function_s *f, expr_t *e);
|
||||
expr_t *at_return_expr (struct function_s *f, expr_t *e);
|
||||
expr_t *conditional_expr (expr_t *cond, expr_t *e1, expr_t *e2);
|
||||
expr_t *incop_expr (int op, expr_t *e, int postop);
|
||||
expr_t *array_expr (expr_t *array, expr_t *index);
|
||||
|
|
|
@ -42,6 +42,7 @@ typedef struct ty_func_s {
|
|||
union {
|
||||
struct {
|
||||
unsigned no_va_list:1;///< don't inject va_list for ... function
|
||||
unsigned void_return:1;///< special handling for return value
|
||||
};
|
||||
unsigned attribute_bits;
|
||||
};
|
||||
|
@ -106,6 +107,7 @@ typedef struct {
|
|||
unsigned is_overload:1;
|
||||
unsigned nosave:1;
|
||||
unsigned no_va_list:1;
|
||||
unsigned void_return:1;
|
||||
};
|
||||
unsigned spec_bits;
|
||||
};
|
||||
|
|
|
@ -2477,6 +2477,30 @@ return_expr (function_t *f, expr_t *e)
|
|||
return new_return_expr (e);
|
||||
}
|
||||
|
||||
expr_t *
|
||||
at_return_expr (function_t *f, expr_t *e)
|
||||
{
|
||||
const type_t *ret_type = unalias_type (f->type->t.func.type);
|
||||
|
||||
if (!is_void(ret_type)) {
|
||||
return error (e, "use of @return in non-void function");
|
||||
}
|
||||
if (is_nil (e)) {
|
||||
// int or pointer 0 seems reasonable
|
||||
return new_return_expr (new_int_expr (0));
|
||||
} else if (!is_function_call (e)) {
|
||||
return error (e, "@return value not a function");
|
||||
}
|
||||
expr_t *call_expr = e->e.block.result->e.branch.target;
|
||||
const type_t *call_type = get_type (call_expr);
|
||||
if (!is_func (call_type) && !call_type->t.func.void_return) {
|
||||
return error (e, "@return function not void_return");
|
||||
}
|
||||
expr_t *ret_expr = new_return_expr (e);
|
||||
ret_expr->e.retrn.at_return = 1;
|
||||
return ret_expr;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
conditional_expr (expr_t *cond, expr_t *e1, expr_t *e2)
|
||||
{
|
||||
|
|
|
@ -392,6 +392,7 @@ static keyword_t qf_keywords[] = {
|
|||
{"@args", ARGS, 0 },
|
||||
{"@va_list", TYPE, &type_va_list },
|
||||
{"@param", TYPE, &type_param },
|
||||
{"@return", AT_RETURN, 0 },
|
||||
|
||||
{"@cross", CROSS, 0 },
|
||||
{"@dot", DOT, 0 },
|
||||
|
|
|
@ -149,7 +149,8 @@ int yylex (void);
|
|||
%token <symbol> CLASS_NAME NAME
|
||||
%token <expr> VALUE STRING
|
||||
|
||||
%token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELLIPSIS
|
||||
%token LOCAL WHILE DO IF ELSE FOR BREAK CONTINUE
|
||||
%token RETURN AT_RETURN ELLIPSIS
|
||||
%token NIL GOTO SWITCH CASE DEFAULT ENUM
|
||||
%token ARGS TYPEDEF EXTERN STATIC SYSTEM OVERLOAD NOT ATTRIBUTE
|
||||
%token UNSIGNED SIGNED LONG SHORT
|
||||
|
@ -251,6 +252,8 @@ parse_attributes (attribute_t *attr_list)
|
|||
spec.no_va_list = 1;
|
||||
} else if (!strcmp (attr->name, "nosave")) {
|
||||
spec.nosave = 1;
|
||||
} else if (!strcmp (attr->name, "void_return")) {
|
||||
spec.void_return = 1;
|
||||
} else {
|
||||
warning (0, "skipping unknown attribute '%s'", attr->name);
|
||||
}
|
||||
|
@ -425,6 +428,7 @@ static void
|
|||
set_func_type_attrs (type_t *func, specifier_t spec)
|
||||
{
|
||||
func->t.func.no_va_list = spec.no_va_list;
|
||||
func->t.func.void_return = spec.void_return;
|
||||
}
|
||||
|
||||
%}
|
||||
|
@ -1420,6 +1424,7 @@ statement
|
|||
| local_def { $$ = $1; }
|
||||
| RETURN opt_expr ';' { $$ = return_expr (current_func, $2); }
|
||||
| RETURN compound_init ';' { $$ = return_expr (current_func, $2); }
|
||||
| AT_RETURN expr ';' { $$ = at_return_expr (current_func, $2); }
|
||||
| BREAK ';'
|
||||
{
|
||||
$$ = 0;
|
||||
|
|
|
@ -1427,11 +1427,18 @@ statement_return (sblock_t *sblock, expr_t *e)
|
|||
if (e->e.retrn.ret_val) {
|
||||
expr_t *ret_val = e->e.retrn.ret_val;
|
||||
type_t *ret_type = get_type (ret_val);
|
||||
s->opa = return_operand (ret_type, e);
|
||||
|
||||
// at_return is used for passing the result of a void_return
|
||||
// function through void. v6 progs always use .return for the
|
||||
// return value, so don't need to do anything special: just call
|
||||
// the function and do a normal void return
|
||||
if (!e->e.retrn.at_return) {
|
||||
s->opa = return_operand (ret_type, e);
|
||||
}
|
||||
sblock = statement_subexpr (sblock, ret_val, &s->opa);
|
||||
}
|
||||
} else {
|
||||
if (e->e.retrn.ret_val) {
|
||||
if (!e->e.retrn.at_return && e->e.retrn.ret_val) {
|
||||
expr_t *ret_val = e->e.retrn.ret_val;
|
||||
type_t *ret_type = get_type (ret_val);
|
||||
pr_ushort_t ret_crtl = type_size (ret_type) - 1;
|
||||
|
@ -1440,6 +1447,21 @@ statement_return (sblock_t *sblock, expr_t *e)
|
|||
ret_crtl |= mode << 5;
|
||||
s->opc = short_operand (ret_crtl, e);
|
||||
} else {
|
||||
if (e->e.retrn.at_return) {
|
||||
expr_t *call = e->e.retrn.ret_val;
|
||||
if (!call || !is_function_call (call)) {
|
||||
internal_error (e, "@return with no call");
|
||||
}
|
||||
// FIXME hard-coded reg, and assumes 3 is free
|
||||
#define REG 3
|
||||
expr_t *with = new_with_expr (11, REG, new_short_expr (0));
|
||||
def_t *ret_ptr = new_def (0, 0, 0, sc_local);
|
||||
operand_t *ret_op = def_operand (ret_ptr, &type_void, e);
|
||||
ret_ptr->reg = REG;
|
||||
expr_file_line (with, e);
|
||||
sblock = statement_slist (sblock, with);
|
||||
sblock = statement_subexpr (sblock, call, &ret_op);
|
||||
}
|
||||
s->opa = short_operand (0, e);
|
||||
s->opb = short_operand (0, e);
|
||||
s->opc = short_operand (-1, e); // void return
|
||||
|
|
Loading…
Reference in a new issue