mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-12 14:23:33 +00:00
Add #must_tail (similar syntax to #force_inline
This commit is contained in:
@@ -8210,7 +8210,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *
|
||||
add_objc_proc_type(c, call, return_type, param_types);
|
||||
}
|
||||
|
||||
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
|
||||
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, ProcTailing tailing, Type *type_hint) {
|
||||
if (proc != nullptr &&
|
||||
proc->kind == Ast_BasicDirective) {
|
||||
ast_node(bd, BasicDirective, proc);
|
||||
@@ -8241,7 +8241,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
return Expr_Expr;
|
||||
}
|
||||
if (inlining != ProcInlining_none) {
|
||||
error(call, "Inlining operators are not allowed on built-in procedures");
|
||||
error(call, "Inlining directives are not allowed on built-in procedures");
|
||||
}
|
||||
if (tailing != ProcTailing_none) {
|
||||
error(call, "Tailing directives are not allowed on built-in procedures");
|
||||
}
|
||||
} else {
|
||||
if (proc != nullptr) {
|
||||
@@ -8383,6 +8386,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
}
|
||||
|
||||
bool is_call_inlined = false;
|
||||
bool is_call_tailed = true;
|
||||
|
||||
switch (inlining) {
|
||||
case ProcInlining_inline:
|
||||
@@ -8417,6 +8421,20 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
}
|
||||
}
|
||||
|
||||
switch (tailing) {
|
||||
case ProcTailing_none:
|
||||
break;
|
||||
case ProcTailing_must_tail:
|
||||
is_call_tailed = true;
|
||||
if (proc != nullptr) {
|
||||
Entity *e = entity_from_expr(proc);
|
||||
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||
// TODO(bill): `preserve_none`
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
String invalid;
|
||||
if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) {
|
||||
@@ -11825,7 +11843,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
|
||||
case_end;
|
||||
|
||||
case_ast_node(ce, CallExpr, node);
|
||||
return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, type_hint);
|
||||
return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, ce->tailing, type_hint);
|
||||
case_end;
|
||||
|
||||
case_ast_node(de, DerefExpr, node);
|
||||
@@ -12566,6 +12584,12 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
|
||||
case_end;
|
||||
|
||||
case_ast_node(ce, CallExpr, node);
|
||||
switch (ce->tailing) {
|
||||
case ProcTailing_must_tail:
|
||||
str = gb_string_appendc(str, "#must_tail ");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ce->inlining) {
|
||||
case ProcInlining_inline:
|
||||
str = gb_string_appendc(str, "#force_inline ");
|
||||
|
||||
@@ -2111,7 +2111,7 @@ gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedur
|
||||
|
||||
for (Entity *e : info->init_procedures) {
|
||||
lbValue value = lb_find_procedure_value_from_entity(m, e);
|
||||
lb_emit_call(p, value, {}, ProcInlining_none);
|
||||
lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none);
|
||||
}
|
||||
|
||||
|
||||
@@ -2157,7 +2157,7 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C
|
||||
|
||||
for (Entity *e : info->fini_procedures) {
|
||||
lbValue value = lb_find_procedure_value_from_entity(main_module, e);
|
||||
lb_emit_call(p, value, {}, ProcInlining_none);
|
||||
lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none);
|
||||
}
|
||||
|
||||
lb_end_procedure_body(p);
|
||||
@@ -2850,7 +2850,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
|
||||
}
|
||||
|
||||
lbValue startup_runtime_value = {startup_runtime->value, startup_runtime->type};
|
||||
lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none);
|
||||
lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none, ProcTailing_none);
|
||||
|
||||
if (build_context.command_kind == Command_test) {
|
||||
Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test"));
|
||||
@@ -2917,16 +2917,16 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
|
||||
|
||||
auto exit_args = array_make<lbValue>(temporary_allocator(), 1);
|
||||
exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1));
|
||||
lb_emit_call(p, exit_runner, exit_args, ProcInlining_none);
|
||||
lb_emit_call(p, exit_runner, exit_args, ProcInlining_none, ProcTailing_none);
|
||||
} else {
|
||||
if (m->info->entry_point != nullptr) {
|
||||
lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
|
||||
lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
|
||||
lb_emit_call(p, entry_point, {}, ProcInlining_no_inline, ProcTailing_none);
|
||||
}
|
||||
|
||||
if (call_cleanup) {
|
||||
lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type};
|
||||
lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
|
||||
lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, ProcTailing_none);
|
||||
}
|
||||
|
||||
if (is_dll_main) {
|
||||
|
||||
@@ -345,6 +345,7 @@ struct lbProcedure {
|
||||
Ast * body;
|
||||
u64 tags;
|
||||
ProcInlining inlining;
|
||||
ProcTailing tailing;
|
||||
bool is_foreign;
|
||||
bool is_export;
|
||||
bool is_entry_point;
|
||||
@@ -484,7 +485,7 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo
|
||||
gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node);
|
||||
gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
|
||||
gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
|
||||
gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none);
|
||||
gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none, ProcTailing tailing = ProcTailing_none);
|
||||
gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
|
||||
gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x);
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
|
||||
p->type_expr = decl->type_expr;
|
||||
p->body = pl->body;
|
||||
p->inlining = pl->inlining;
|
||||
p->tailing = pl->tailing;
|
||||
p->is_foreign = entity->Procedure.is_foreign;
|
||||
p->is_export = entity->Procedure.is_export;
|
||||
p->is_entry_point = false;
|
||||
@@ -178,6 +179,12 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
|
||||
}
|
||||
}
|
||||
|
||||
switch (p->tailing) {
|
||||
case ProcTailing_must_tail:
|
||||
lb_add_attribute_to_proc(m, p->value, "preserve_none");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (entity->Procedure.optimization_mode) {
|
||||
case ProcedureOptimizationMode_None:
|
||||
lb_add_attribute_to_proc(m, p->value, "optnone");
|
||||
@@ -387,6 +394,7 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name
|
||||
p->body = nullptr;
|
||||
p->tags = 0;
|
||||
p->inlining = ProcInlining_none;
|
||||
p->tailing = ProcTailing_none;
|
||||
p->is_foreign = false;
|
||||
p->is_export = false;
|
||||
p->is_entry_point = false;
|
||||
@@ -855,7 +863,7 @@ gb_internal Array<lbValue> lb_value_to_array(lbProcedure *p, gbAllocator const &
|
||||
|
||||
|
||||
|
||||
gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining) {
|
||||
gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining, ProcTailing tailing) {
|
||||
GB_ASSERT(p->module->ctx == LLVMGetTypeContext(LLVMTypeOf(value.value)));
|
||||
|
||||
unsigned arg_count = cast(unsigned)processed_args.count;
|
||||
@@ -972,6 +980,15 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
|
||||
break;
|
||||
}
|
||||
|
||||
switch (tailing) {
|
||||
case ProcTailing_none:
|
||||
break;
|
||||
case ProcTailing_must_tail:
|
||||
LLVMSetTailCall(ret, true);
|
||||
LLVMSetTailCallKind(ret, LLVMTailCallKindMustTail);
|
||||
break;
|
||||
}
|
||||
|
||||
lbValue res = {};
|
||||
res.value = ret;
|
||||
res.type = abi_rt;
|
||||
@@ -1045,7 +1062,7 @@ gb_internal lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) {
|
||||
return lb_emit_load(p, res);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining) {
|
||||
gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining, ProcTailing tailing) {
|
||||
lbModule *m = p->module;
|
||||
|
||||
Type *pt = base_type(value.type);
|
||||
@@ -1168,10 +1185,10 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
|
||||
|
||||
if (return_by_pointer) {
|
||||
lbValue return_ptr = lb_add_local_generated(p, rt, true).addr;
|
||||
lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining);
|
||||
lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining, tailing);
|
||||
result = lb_emit_load(p, return_ptr);
|
||||
} else if (rt != nullptr) {
|
||||
result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining);
|
||||
result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining, tailing);
|
||||
if (ft->ret.cast_type) {
|
||||
result.value = OdinLLVMBuildTransmute(p, result.value, ft->ret.cast_type);
|
||||
}
|
||||
@@ -1184,7 +1201,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
|
||||
result = lb_emit_conv(p, result, rt);
|
||||
}
|
||||
} else {
|
||||
lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining);
|
||||
lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining, tailing);
|
||||
}
|
||||
|
||||
if (original_rt != rt) {
|
||||
@@ -4402,6 +4419,25 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
return lb_handle_objc_auto_send(p, expr, slice(call_args, 0, call_args.count));
|
||||
}
|
||||
|
||||
return lb_emit_call(p, value, call_args, ce->inlining);
|
||||
|
||||
ProcInlining inlining = ce->inlining;
|
||||
ProcTailing tailing = ce->tailing;
|
||||
|
||||
if (tailing == ProcTailing_none &&
|
||||
proc_entity && proc_entity->kind == Entity_Procedure &&
|
||||
proc_entity->decl_info &&
|
||||
proc_entity->decl_info->proc_lit) {
|
||||
ast_node(pl, ProcLit, proc_entity->decl_info->proc_lit);
|
||||
|
||||
if (pl->inlining != ProcInlining_none) {
|
||||
inlining = pl->inlining;
|
||||
}
|
||||
|
||||
if (pl->tailing != ProcTailing_none) {
|
||||
tailing = pl->tailing;
|
||||
}
|
||||
}
|
||||
|
||||
return lb_emit_call(p, value, call_args, inlining, tailing);
|
||||
}
|
||||
|
||||
|
||||
@@ -2176,7 +2176,7 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) {
|
||||
return x.pos.line == y.pos.line;
|
||||
}
|
||||
|
||||
gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) {
|
||||
gb_internal Ast *parse_inlining_or_tailing_operand(AstFile *f, Token token) {
|
||||
Ast *expr = parse_unary_expr(f, false);
|
||||
Ast *e = strip_or_return_expr(expr);
|
||||
if (e == nullptr) {
|
||||
@@ -2187,11 +2187,14 @@ gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) {
|
||||
return ast_bad_expr(f, token, f->curr_token);
|
||||
}
|
||||
ProcInlining pi = ProcInlining_none;
|
||||
ProcTailing pt = ProcTailing_none;
|
||||
if (token.kind == Token_Ident) {
|
||||
if (token.string == "force_inline") {
|
||||
pi = ProcInlining_inline;
|
||||
} else if (token.string == "force_no_inline") {
|
||||
pi = ProcInlining_no_inline;
|
||||
} else if (token.string == "must_tail") {
|
||||
pt = ProcTailing_must_tail;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2211,6 +2214,14 @@ gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) {
|
||||
}
|
||||
}
|
||||
|
||||
if (pt != ProcTailing_none) {
|
||||
if (e->kind == Ast_ProcLit) {
|
||||
e->ProcLit.tailing = pt;
|
||||
} else if (e->kind == Ast_CallExpr) {
|
||||
e->CallExpr.tailing = pt;
|
||||
}
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -2507,8 +2518,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
syntax_error(tag, "#relative types have now been removed in favour of \"core:relative\"");
|
||||
return ast_relative_type(f, tag, type);
|
||||
} else if (name.string == "force_inline" ||
|
||||
name.string == "force_no_inline") {
|
||||
return parse_force_inlining_operand(f, name);
|
||||
name.string == "force_no_inline" ||
|
||||
name.string == "must_tail") {
|
||||
return parse_inlining_or_tailing_operand(f, name);
|
||||
}
|
||||
return ast_basic_directive(f, token, name);
|
||||
}
|
||||
@@ -5399,8 +5411,9 @@ gb_internal Ast *parse_stmt(AstFile *f) {
|
||||
expect_semicolon(f);
|
||||
return stmt;
|
||||
} else if (name.string == "force_inline" ||
|
||||
name.string == "force_no_inline") {
|
||||
Ast *expr = parse_force_inlining_operand(f, name);
|
||||
name.string == "force_no_inline" ||
|
||||
name.string == "must_tail") {
|
||||
Ast *expr = parse_inlining_or_tailing_operand(f, name);
|
||||
Ast *stmt = ast_expr_stmt(f, expr);
|
||||
expect_semicolon(f);
|
||||
return stmt;
|
||||
|
||||
@@ -263,12 +263,17 @@ struct ForeignFileWorkerData {
|
||||
|
||||
|
||||
|
||||
enum ProcInlining {
|
||||
ProcInlining_none = 0,
|
||||
ProcInlining_inline = 1,
|
||||
enum ProcInlining : u8 {
|
||||
ProcInlining_none = 0,
|
||||
ProcInlining_inline = 1,
|
||||
ProcInlining_no_inline = 2,
|
||||
};
|
||||
|
||||
enum ProcTailing : u8 {
|
||||
ProcTailing_none = 0,
|
||||
ProcTailing_must_tail = 1,
|
||||
};
|
||||
|
||||
enum ProcTag {
|
||||
ProcTag_bounds_check = 1<<0,
|
||||
ProcTag_no_bounds_check = 1<<1,
|
||||
@@ -441,6 +446,7 @@ struct AstSplitArgs {
|
||||
Ast *body; \
|
||||
u64 tags; \
|
||||
ProcInlining inlining; \
|
||||
ProcTailing tailing; \
|
||||
Token where_token; \
|
||||
Slice<Ast *> where_clauses; \
|
||||
DeclInfo *decl; \
|
||||
@@ -486,6 +492,7 @@ AST_KIND(_ExprBegin, "", bool) \
|
||||
Token close; \
|
||||
Token ellipsis; \
|
||||
ProcInlining inlining; \
|
||||
ProcTailing tailing; \
|
||||
bool optional_ok_one; \
|
||||
bool was_selector; \
|
||||
AstSplitArgs *split_args; \
|
||||
|
||||
Reference in New Issue
Block a user