Add attributes to call site. @inline, @noinline to calls #250

This commit is contained in:
Christoffer Lerno
2021-08-03 11:24:57 +02:00
committed by Christoffer Lerno
parent 78425ff5f2
commit 490dd65664
14 changed files with 121 additions and 39 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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");

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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)
{

View File

@@ -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);

View 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 }

View File

@@ -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