From 9adec628c1c6b3d24f7a8642bbf5c0c84586d161 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 14 Apr 2021 17:15:28 +0100 Subject: [PATCH] Add `@(cold)` attribute to procedure declarations --- src/check_decl.cpp | 4 ++++ src/check_expr.cpp | 30 +++++++++++++++--------------- src/checker.cpp | 12 ++++++++++++ src/checker.hpp | 1 + src/entity.cpp | 9 +++++---- src/llvm_backend.cpp | 35 +++++++++++++++++++++++++++++++++-- src/llvm_backend.hpp | 7 +++++++ src/llvm_backend_opt.cpp | 11 +++++------ 8 files changed, 82 insertions(+), 27 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 5e52597b9..51c0b6ee5 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -690,6 +690,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (ac.test) { e->flags |= EntityFlag_Test; } + if (ac.set_cold) { + e->flags |= EntityFlag_Cold; + } + e->Procedure.is_export = ac.is_export; e->deprecated_message = ac.deprecated_message; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3b7a975d1..2114746a3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8175,24 +8175,24 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr } switch (inlining) { - case ProcInlining_inline: { - if (proc != nullptr) { - Entity *e = entity_from_expr(proc); - if (e != nullptr && e->kind == Entity_Procedure) { - DeclInfo *decl = e->decl_info; - if (decl->proc_lit) { - ast_node(pl, ProcLit, decl->proc_lit); - if (pl->inlining == ProcInlining_no_inline) { - error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'"); - } + case ProcInlining_inline: { + if (proc != nullptr) { + Entity *e = entity_from_expr(proc); + if (e != nullptr && e->kind == Entity_Procedure) { + DeclInfo *decl = e->decl_info; + if (decl->proc_lit) { + ast_node(pl, ProcLit, decl->proc_lit); + if (pl->inlining == ProcInlining_no_inline) { + error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'"); } } } - break; } + break; + } - case ProcInlining_no_inline: - break; + case ProcInlining_no_inline: + break; } operand->expr = call; @@ -11019,10 +11019,10 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { case_ast_node(ce, CallExpr, node); switch (ce->inlining) { case ProcInlining_inline: - str = gb_string_appendc(str, "inline "); + str = gb_string_appendc(str, "#force_inline "); break; case ProcInlining_no_inline: - str = gb_string_appendc(str, "no_inline "); + str = gb_string_appendc(str, "#force_no_inline "); break; } diff --git a/src/checker.cpp b/src/checker.cpp index 0111872b9..e0b303369 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2562,6 +2562,18 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a boolean value for '%.*s'", LIT(name)); } return true; + } else if (name == "cold") { + if (value == nullptr) { + ac->set_cold = true; + } else { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Bool) { + ac->set_cold = ev.value_bool; + } else { + error(elem, "Expected a boolean value for '%.*s' or no value whatsoever", LIT(name)); + } + } + return true; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index abdb601a9..b3e0b60ec 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -105,6 +105,7 @@ struct AttributeContext { bool has_disabled_proc; bool disabled_proc; bool test; + bool set_cold; String link_name; String link_prefix; isize init_expr_list_count; diff --git a/src/entity.cpp b/src/entity.cpp index 2786fcc6d..a27b7cb37 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -32,7 +32,7 @@ String const entity_strings[] = { #undef ENTITY_KIND }; -enum EntityFlag : u32 { +enum EntityFlag : u64 { EntityFlag_Visited = 1<<0, EntityFlag_Used = 1<<1, EntityFlag_Using = 1<<2, @@ -63,12 +63,13 @@ enum EntityFlag : u32 { EntityFlag_AutoCast = 1<<22, EntityFlag_Disabled = 1<<24, + EntityFlag_Cold = 1<<25, // procedure is rarely called - EntityFlag_Test = 1<<25, + EntityFlag_Test = 1<<30, }; -enum EntityState { +enum EntityState : u32 { EntityState_Unresolved = 0, EntityState_InProgress = 1, EntityState_Resolved = 2, @@ -98,7 +99,7 @@ struct ParameterValue { struct Entity { EntityKind kind; u64 id; - u32 flags; + u64 flags; EntityState state; Token token; Scope * scope; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index eb4f3c300..16c2e35e5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2464,9 +2464,12 @@ void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *nam } void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name) { - lb_add_proc_attribute_at_index(p, index, name, cast(u64)true); + lb_add_proc_attribute_at_index(p, index, name, 0); } +void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value, char const *name, u64 value=0) { + LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value)); +} void lb_ensure_abi_function_type(lbModule *m, lbProcedure *p) { @@ -2556,6 +2559,23 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { LLVMSetFunctionCallConv(p->value, cc_kind); } + if (entity->flags & EntityFlag_Cold) { + lb_add_attribute_to_proc(m, p->value, "cold"); + } + + if (pt->Proc.diverging) { + lb_add_attribute_to_proc(m, p->value, "noreturn"); + } + + switch (p->inlining) { + case ProcInlining_inline: + lb_add_attribute_to_proc(m, p->value, "alwaysinline"); + break; + case ProcInlining_no_inline: + lb_add_attribute_to_proc(m, p->value, "noinline"); + break; + } + // lbCallingConventionKind cc_kind = lbCallingConvention_C; // // TODO(bill): Clean up this logic // if (build_context.metrics.os != TargetOs_js) { @@ -8073,7 +8093,18 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, } LLVMValueRef ret = LLVMBuildCall2(p->builder, fnp, fn, args, arg_count, ""); - // LLVMValueRef ret = LLVMBuildCall(p->builder, fn, args, arg_count, ""); + + switch (inlining) { + case ProcInlining_none: + break; + case ProcInlining_inline: + // LLVMAddAttributeAtIndex(ret, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(p->module->ctx, "alwaysinline")); + break; + case ProcInlining_no_inline: + // LLVMAddAttributeAtIndex(ret, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(p->module->ctx, "noinline")); + break; + } + lbValue res = {}; res.value = ret; res.type = abi_rt; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 5046eee8c..8117271c1 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -499,3 +499,10 @@ enum { DW_TAG_subroutine_type = 21, DW_TAG_inheritance = 28, }; + + +enum : LLVMAttributeIndex { + LLVMAttributeIndex_ReturnIndex = 0u, + LLVMAttributeIndex_FunctionIndex = ~0u, + LLVMAttributeIndex_FirstArgIndex = 1, +}; diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 0e863f9e9..5e1154af2 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -107,12 +107,11 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa if (optimization_level >= 2) { // NOTE(bill, 2021-03-29: use this causes invalid code generation) - LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate(); - LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level); - LLVMPassManagerBuilderPopulateModulePassManager(pmb, mpm); - LLVMPassManagerBuilderPopulateLTOPassManager(pmb, mpm, false, true); - // LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level); - return; + // LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate(); + // LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level); + // LLVMPassManagerBuilderPopulateModulePassManager(pmb, mpm); + // LLVMPassManagerBuilderPopulateLTOPassManager(pmb, mpm, false, true); + // return; } LLVMAddIPSCCPPass(mpm);