Browse Source

Add `@(cold)` attribute to procedure declarations

gingerBill 4 years ago
parent
commit
9adec628c1
8 changed files with 82 additions and 27 deletions
  1. 4 0
      src/check_decl.cpp
  2. 15 15
      src/check_expr.cpp
  3. 12 0
      src/checker.cpp
  4. 1 0
      src/checker.hpp
  5. 5 4
      src/entity.cpp
  6. 33 2
      src/llvm_backend.cpp
  7. 7 0
      src/llvm_backend.hpp
  8. 5 6
      src/llvm_backend_opt.cpp

+ 4 - 0
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);

+ 15 - 15
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;
 		}
 

+ 12 - 0
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;
 }

+ 1 - 0
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;

+ 5 - 4
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;

+ 33 - 2
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;

+ 7 - 0
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,
+};

+ 5 - 6
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);