mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Add attributes to call site. @inline, @noinline to calls #250
This commit is contained in:
committed by
Christoffer Lerno
parent
78425ff5f2
commit
490dd65664
@@ -616,6 +616,8 @@ typedef struct
|
||||
bool is_type_method : 1;
|
||||
bool is_pointer_call : 1;
|
||||
bool unsplat_last : 1;
|
||||
bool force_inline : 1;
|
||||
bool force_noinline : 1;
|
||||
union
|
||||
{
|
||||
Expr *function;
|
||||
@@ -624,6 +626,7 @@ typedef struct
|
||||
Expr **arguments;
|
||||
Decl **body_arguments;
|
||||
Ast *body;
|
||||
Attr **attributes;
|
||||
} ExprCall;
|
||||
|
||||
typedef struct
|
||||
|
||||
@@ -596,6 +596,7 @@ typedef enum
|
||||
ATTR_TYPEDEF = 1 << 7,
|
||||
ATTR_MEMBER = 1 << 8,
|
||||
ATTR_INTERFACE = 1 << 9,
|
||||
ATTR_CALL = 1 << 10,
|
||||
} AttributeDomain;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -585,6 +585,7 @@ unsigned intrinsic_id_lifetime_end;
|
||||
|
||||
|
||||
unsigned attribute_noinline;
|
||||
unsigned attribute_optnone;
|
||||
unsigned attribute_alwaysinline;
|
||||
unsigned attribute_inlinehint;
|
||||
unsigned attribute_noreturn;
|
||||
@@ -665,6 +666,7 @@ void llvm_codegen_setup()
|
||||
intrinsic_id_umin = lookup_intrinsic("llvm.umin");
|
||||
|
||||
attribute_noinline = lookup_attribute("noinline");
|
||||
attribute_optnone = lookup_attribute("optnone");
|
||||
attribute_alwaysinline = lookup_attribute("alwaysinline");
|
||||
attribute_inlinehint = lookup_attribute("inlinehint");
|
||||
attribute_noreturn = lookup_attribute("noreturn");
|
||||
|
||||
@@ -2720,6 +2720,8 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
LLVMValueRef func;
|
||||
BEValue temp_value;
|
||||
|
||||
bool always_inline = false;
|
||||
|
||||
// 1. Call through a pointer.
|
||||
if (expr->call_expr.is_pointer_call)
|
||||
{
|
||||
@@ -2745,7 +2747,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
// 2a. Get the function declaration
|
||||
Decl *function_decl = expr->call_expr.func_ref;
|
||||
|
||||
always_inline = function_decl->func_decl.attr_inline;
|
||||
if (function_decl->func_decl.is_builtin)
|
||||
{
|
||||
gencontext_emit_call_intrinsic_expr(c, result_value, expr);
|
||||
@@ -2907,7 +2909,17 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
// 10. Create the actual call (remember to emit a loc, because we might have shifted loc emitting the params)
|
||||
EMIT_LOC(c, expr);
|
||||
LLVMValueRef call_value = LLVMBuildCall2(c->builder, func_type, func, values, vec_size(values), "");
|
||||
|
||||
if (expr->call_expr.force_noinline)
|
||||
{
|
||||
llvm_attribute_add_call(c, call_value, attribute_noinline, -1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expr->call_expr.force_inline || always_inline)
|
||||
{
|
||||
llvm_attribute_add_call(c, call_value, attribute_alwaysinline, -1, 0);
|
||||
}
|
||||
}
|
||||
// 11. Process the return value.
|
||||
switch (ret_info->kind)
|
||||
{
|
||||
|
||||
@@ -557,10 +557,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl)
|
||||
ABIArgInfo *info = param->var.abi_info;
|
||||
llvm_emit_param_attributes(c, function, info, false, info->param_index_start + 1, info->param_index_end);
|
||||
}
|
||||
if (decl->func_decl.attr_inline)
|
||||
{
|
||||
llvm_attribute_add(c, function, attribute_alwaysinline, -1);
|
||||
}
|
||||
// We ignore decl->func_decl.attr_inline and place it in every call instead.
|
||||
if (decl->func_decl.attr_noinline)
|
||||
{
|
||||
llvm_attribute_add(c, function, attribute_noinline, -1);
|
||||
|
||||
@@ -162,6 +162,7 @@ extern unsigned intrinsic_id_lifetime_end;
|
||||
|
||||
// LLVM Attributes
|
||||
extern unsigned attribute_noinline; // No function inlining
|
||||
extern unsigned attribute_optnone; // No optimization
|
||||
extern unsigned attribute_alwaysinline; // Force inlining
|
||||
extern unsigned attribute_inlinehint; // "Inline possibly"
|
||||
extern unsigned attribute_noreturn; // No function return
|
||||
|
||||
@@ -449,6 +449,7 @@ static Expr *parse_call_expr(Context *context, Expr *left)
|
||||
{
|
||||
call->call_expr.body = TRY_AST_OR(parse_compound_stmt(context), poisoned_expr);
|
||||
}
|
||||
if (!parse_attributes(context, &call->call_expr.attributes)) return false;
|
||||
return call;
|
||||
}
|
||||
|
||||
|
||||
@@ -960,9 +960,9 @@ bool parse_next_is_case_type(Context *context)
|
||||
*
|
||||
* @return true if parsing succeeded, false if recovery is needed
|
||||
*/
|
||||
static inline bool parse_attributes(Context *context, Decl *parent_decl)
|
||||
bool parse_attributes(Context *context, Attr ***attributes_ref)
|
||||
{
|
||||
parent_decl->attributes = NULL;
|
||||
*attributes_ref = NULL;
|
||||
|
||||
while (try_consume(context, TOKEN_AT))
|
||||
{
|
||||
@@ -982,16 +982,16 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl)
|
||||
attr->expr = TRY_EXPR_OR(parse_const_paren_expr(context), false);
|
||||
}
|
||||
const char *name = TOKSTR(attr->name);
|
||||
VECEACH(parent_decl->attributes, i)
|
||||
VECEACH(*attributes_ref, i)
|
||||
{
|
||||
Attr *other_attr = parent_decl->attributes[i];
|
||||
Attr *other_attr = *attributes_ref[i];
|
||||
if (TOKSTR(other_attr->name) == name)
|
||||
{
|
||||
SEMA_TOKID_ERROR(attr->name, "Repeat of attribute '%s' here.", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
parent_decl->attributes = VECADD(parent_decl->attributes, attr);
|
||||
*attributes_ref = VECADD(*attributes_ref, attr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1029,7 +1029,7 @@ static inline Decl *parse_global_declaration(Context *context, Visibility visibi
|
||||
CONSUME_OR(TOKEN_IDENT, poisoned_decl);
|
||||
}
|
||||
|
||||
if (!parse_attributes(context, decl)) return poisoned_decl;
|
||||
if (!parse_attributes(context, &decl->attributes)) return poisoned_decl;
|
||||
if (try_consume(context, TOKEN_EQ))
|
||||
{
|
||||
decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), poisoned_decl);
|
||||
@@ -1272,7 +1272,7 @@ bool parse_struct_body(Context *context, Decl *parent)
|
||||
member->span.loc = context->prev_tok;
|
||||
advance_and_verify(context, TOKEN_IDENT);
|
||||
}
|
||||
if (!parse_attributes(context, member)) return false;
|
||||
if (!parse_attributes(context, &member->attributes)) return false;
|
||||
if (!parse_struct_body(context, member))
|
||||
{
|
||||
decl_poison(parent);
|
||||
@@ -1317,7 +1317,7 @@ bool parse_struct_body(Context *context, Decl *parent)
|
||||
return false;
|
||||
}
|
||||
advance(context);
|
||||
if (!parse_attributes(context, member)) return false;
|
||||
if (!parse_attributes(context, &member->attributes)) return false;
|
||||
if (!try_consume(context, TOKEN_COMMA)) break;
|
||||
if (was_inline)
|
||||
{
|
||||
@@ -1351,7 +1351,7 @@ static inline Decl *parse_struct_declaration(Context *context, Visibility visibi
|
||||
if (!consume_type_name(context, type_name)) return poisoned_decl;
|
||||
Decl *decl = decl_new_with_type(name, decl_from_token(type), visibility);
|
||||
|
||||
if (!parse_attributes(context, decl))
|
||||
if (!parse_attributes(context, &decl->attributes))
|
||||
{
|
||||
return poisoned_decl;
|
||||
}
|
||||
@@ -1614,19 +1614,6 @@ static inline Decl *parse_define(Context *context, Visibility visibility)
|
||||
}
|
||||
|
||||
|
||||
static AttributeDomain TOKEN_TO_ATTR[TOKEN_EOF + 1] = {
|
||||
[TOKEN_FUNC] = ATTR_FUNC,
|
||||
[TOKEN_VAR] = ATTR_VAR,
|
||||
[TOKEN_ENUM] = ATTR_ENUM,
|
||||
[TOKEN_STRUCT] = ATTR_STRUCT,
|
||||
[TOKEN_INTERFACE] = ATTR_INTERFACE,
|
||||
[TOKEN_UNION] = ATTR_UNION,
|
||||
[TOKEN_CONST] = ATTR_CONST,
|
||||
[TOKEN_DEFINE] = ATTR_TYPEDEF,
|
||||
[TOKEN_ERRTYPE] = ATTR_ERROR,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* func_header ::= type '!'? (type '.')? IDENT
|
||||
* macro_header ::= (type '!'?)? (type '.')? IDENT
|
||||
@@ -1895,7 +1882,7 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit
|
||||
RANGE_EXTEND_PREV(func);
|
||||
if (!parse_parameter_list(context, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl;
|
||||
|
||||
if (!parse_attributes(context, func)) return poisoned_decl;
|
||||
if (!parse_attributes(context, &func->attributes)) return poisoned_decl;
|
||||
|
||||
// TODO remove
|
||||
is_interface = TOKEN_IS(TOKEN_EOS);
|
||||
@@ -1934,7 +1921,7 @@ static inline Decl *parse_interface_declaration(Context *context, Visibility vis
|
||||
if (!consume_type_name(context, "interface")) return poisoned_decl;
|
||||
Decl *decl = decl_new_with_type(name, DECL_INTERFACE, visibility);
|
||||
|
||||
if (!parse_attributes(context, decl))
|
||||
if (!parse_attributes(context, &decl->attributes))
|
||||
{
|
||||
return poisoned_decl;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ void recover_top_level(Context *context);
|
||||
Expr *parse_decl_expr_list(Context *context);
|
||||
Ast* parse_compound_stmt(Context *context);
|
||||
Ast *parse_jump_stmt_no_eos(Context *context);
|
||||
bool parse_attributes(Context *context, Attr ***attributes_ref);
|
||||
|
||||
bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type,
|
||||
bool allow_multiple_values);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "sema_internal.h"
|
||||
|
||||
|
||||
static AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain);
|
||||
|
||||
|
||||
static bool sema_analyse_struct_union(Context *context, Decl *decl);
|
||||
@@ -713,10 +712,12 @@ static const char *attribute_domain_to_string(AttributeDomain domain)
|
||||
return "error type";
|
||||
case ATTR_TYPEDEF:
|
||||
return "typedef";
|
||||
case ATTR_CALL:
|
||||
return "call";
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
static AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain)
|
||||
AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain)
|
||||
{
|
||||
AttributeType type = attribute_by_name(attr);
|
||||
if (type == ATTRIBUTE_NONE)
|
||||
@@ -726,16 +727,16 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib
|
||||
}
|
||||
static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = {
|
||||
[ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_VAR,
|
||||
[ATTRIBUTE_EXTNAME] = ~0,
|
||||
[ATTRIBUTE_EXTNAME] = ~ATTR_CALL,
|
||||
[ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_VAR,
|
||||
[ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION | ATTR_ERROR,
|
||||
[ATTRIBUTE_NORETURN] = ATTR_FUNC,
|
||||
[ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER,
|
||||
[ATTRIBUTE_INLINE] = ATTR_FUNC,
|
||||
[ATTRIBUTE_NOINLINE] = ATTR_FUNC,
|
||||
[ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL,
|
||||
[ATTRIBUTE_NOINLINE] = ATTR_FUNC | ATTR_CALL,
|
||||
[ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION,
|
||||
[ATTRIBUTE_USED] = ~0,
|
||||
[ATTRIBUTE_UNUSED] = ~0,
|
||||
[ATTRIBUTE_USED] = ~ATTR_CALL,
|
||||
[ATTRIBUTE_UNUSED] = ~ATTR_CALL,
|
||||
[ATTRIBUTE_NAKED] = ATTR_FUNC,
|
||||
[ATTRIBUTE_CDECL] = ATTR_FUNC,
|
||||
[ATTRIBUTE_STDCALL] = ATTR_FUNC,
|
||||
|
||||
@@ -1621,6 +1621,29 @@ static bool sema_analyse_body_expansion(Context *context, Expr *call)
|
||||
|
||||
bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl *decl, Expr *struct_var, bool is_macro)
|
||||
{
|
||||
int force_inline = -1;
|
||||
VECEACH(expr->call_expr.attributes, i)
|
||||
{
|
||||
Attr *attr = expr->call_expr.attributes[i];
|
||||
AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_CALL);
|
||||
if (attribute == ATTRIBUTE_NONE) return expr_poison(expr);
|
||||
|
||||
bool had = false;
|
||||
switch (attribute)
|
||||
{
|
||||
case ATTRIBUTE_INLINE:
|
||||
case ATTRIBUTE_NOINLINE:
|
||||
if (decl->decl_kind != DECL_FUNC)
|
||||
{
|
||||
SEMA_TOKID_ERROR(attr->name, "Inline / noinline attribute is only allowed for direct function/method calls");
|
||||
return expr_poison(expr);
|
||||
}
|
||||
force_inline = attribute == ATTRIBUTE_INLINE ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
expr->call_expr.is_type_method = struct_var != NULL;
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
@@ -1647,6 +1670,8 @@ bool sema_expr_analyse_general_call(Context *context, Type *to, Expr *expr, Decl
|
||||
return false;
|
||||
}
|
||||
expr->call_expr.func_ref = decl;
|
||||
expr->call_expr.force_inline = force_inline == 1;
|
||||
expr->call_expr.force_noinline = force_inline == 0;
|
||||
return sema_expr_analyse_func_call(context, to, expr, decl, struct_var);
|
||||
case DECL_GENERIC:
|
||||
if (is_macro)
|
||||
@@ -1670,6 +1695,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr
|
||||
expr->constant = false;
|
||||
expr->pure = false;
|
||||
Expr *func_expr = expr->call_expr.function;
|
||||
|
||||
if (!sema_analyse_expr_value(context, NULL, func_expr)) return false;
|
||||
if (func_expr->expr_kind == EXPR_MACRO_BODY_EXPANSION)
|
||||
{
|
||||
|
||||
@@ -52,3 +52,4 @@ void context_change_scope_with_flags(Context *context, ScopeFlags flags);
|
||||
#define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK()
|
||||
|
||||
void c_abi_func_create(FunctionSignature *signature);
|
||||
AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain);
|
||||
|
||||
49
test/test_suite/expressions/call_inline.c3t
Normal file
49
test/test_suite/expressions/call_inline.c3t
Normal file
@@ -0,0 +1,49 @@
|
||||
module inlineme;
|
||||
import std::io;
|
||||
|
||||
func void test1() @inline
|
||||
{
|
||||
io::println("Inline!");
|
||||
}
|
||||
|
||||
func void test2() @noinline
|
||||
{
|
||||
io::println("No inline!");
|
||||
}
|
||||
|
||||
func void test3()
|
||||
{
|
||||
io::println("Plain");
|
||||
}
|
||||
|
||||
func void main()
|
||||
{
|
||||
test1() @inline;
|
||||
test2() @inline;
|
||||
test3() @inline;
|
||||
test1() @noinline;
|
||||
test2() @noinline;
|
||||
test3() @noinline;
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
}
|
||||
// #expect: inlineme.ll
|
||||
|
||||
entry:
|
||||
call void @inlineme.test1() #2
|
||||
call void @inlineme.test2() #2
|
||||
call void @inlineme.test3() #2
|
||||
call void @inlineme.test1() #3
|
||||
call void @inlineme.test2() #3
|
||||
call void @inlineme.test3() #3
|
||||
call void @inlineme.test1() #2
|
||||
call void @inlineme.test2()
|
||||
call void @inlineme.test3()
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind }
|
||||
attributes #1 = { noinline nounwind }
|
||||
attributes #2 = { alwaysinline }
|
||||
attributes #3 = { noinline }
|
||||
@@ -341,9 +341,9 @@ define i32 @test.test1(i32 %0, i32 %1)
|
||||
ret i32 %6
|
||||
}
|
||||
|
||||
declare void @test_virtual3(i64, i8*) #1
|
||||
declare void @test_virtual3(i64, i8*)
|
||||
|
||||
declare void @test_virtual2(i8*, i8*) #1
|
||||
declare void @test_virtual2(i8*, i8*)
|
||||
|
||||
define i32 @test.sum_us(i64 %0, i8* %1)
|
||||
%x = alloca %"int[]", align 8
|
||||
|
||||
Reference in New Issue
Block a user