Skip to content

Commit

Permalink
Implement branch prediction annotations (RFC 30) (#1528)
Browse files Browse the repository at this point in the history
This change adds annotations to control structures allowing programmers
to make the optimiser aware of the likelihood of a given condition.

Closes #1500.
  • Loading branch information
Benoit Vey authored and sylvanc committed Jan 25, 2017
1 parent 799e1ee commit f9c12da
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 26 deletions.
57 changes: 52 additions & 5 deletions src/libponyc/codegen/gencontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
#include "genname.h"
#include "../pass/expr.h"
#include "../type/subtype.h"
#include "../../libponyrt/mem/pool.h"
#include <assert.h>


LLVMValueRef gen_seq(compile_t* c, ast_t* ast)
{
ast_t* child = ast_child(ast);
Expand Down Expand Up @@ -70,7 +72,9 @@ LLVMValueRef gen_if(compile_t* c, ast_t* ast)
post_block = codegen_block(c, "if_post");

LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, "");
LLVMBuildCondBr(c->builder, test, then_block, else_block);
LLVMValueRef br = LLVMBuildCondBr(c->builder, test, then_block, else_block);

handle_branch_prediction_default(c->context, br, ast);

// Left branch.
LLVMPositionBuilderAtEnd(c->builder, then_block);
Expand Down Expand Up @@ -194,7 +198,9 @@ LLVMValueRef gen_while(compile_t* c, ast_t* ast)
return NULL;

LLVMValueRef test = LLVMBuildTrunc(c->builder, i_value, c->i1, "");
LLVMBuildCondBr(c->builder, test, body_block, else_block);
LLVMValueRef br = LLVMBuildCondBr(c->builder, test, body_block, else_block);

handle_branch_prediction_default(c->context, br, ast);

// Body.
LLVMPositionBuilderAtEnd(c->builder, body_block);
Expand All @@ -221,7 +227,9 @@ LLVMValueRef gen_while(compile_t* c, ast_t* ast)

body_from = LLVMGetInsertBlock(c->builder);
LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, "");
LLVMBuildCondBr(c->builder, test, body_block, post_block);
br = LLVMBuildCondBr(c->builder, test, body_block, post_block);

handle_branch_prediction_default(c->context, br, ast);
}

// Don't need loop status for the else block.
Expand Down Expand Up @@ -327,7 +335,9 @@ LLVMValueRef gen_repeat(compile_t* c, ast_t* ast)

body_from = LLVMGetInsertBlock(c->builder);
LLVMValueRef test = LLVMBuildTrunc(c->builder, c_value, c->i1, "");
LLVMBuildCondBr(c->builder, test, post_block, body_block);
LLVMValueRef br = LLVMBuildCondBr(c->builder, test, post_block, body_block);

handle_branch_prediction_default(c->context, br, cond);
}

// cond block
Expand All @@ -337,7 +347,9 @@ LLVMValueRef gen_repeat(compile_t* c, ast_t* ast)
LLVMValueRef i_value = gen_expr(c, cond);

LLVMValueRef test = LLVMBuildTrunc(c->builder, i_value, c->i1, "");
LLVMBuildCondBr(c->builder, test, else_block, body_block);
LLVMValueRef br = LLVMBuildCondBr(c->builder, test, else_block, body_block);

handle_branch_prediction_default(c->context, br, cond);

// Don't need loop status for the else block.
codegen_poploop(c);
Expand Down Expand Up @@ -605,3 +617,38 @@ LLVMValueRef gen_error(compile_t* c, ast_t* ast)

return GEN_NOVALUE;
}

void attach_branchweights_metadata(LLVMContextRef ctx, LLVMValueRef branch,
unsigned int weights[], unsigned int count)
{
size_t alloc_index = ponyint_pool_index((count + 1) * sizeof(LLVMValueRef));

LLVMValueRef* params = (LLVMValueRef*)ponyint_pool_alloc(alloc_index);

const char str[] = "branch_weights";
params[0] = LLVMMDStringInContext(ctx, str, sizeof(str) - 1);

for(size_t i = 0; i < count; i++)
params[i+1] = LLVMConstInt(LLVMInt32TypeInContext(ctx), weights[i], false);

LLVMValueRef metadata = LLVMMDNodeInContext(ctx, params, count + 1);
const char id[] = "prof";
LLVMSetMetadata(branch, LLVMGetMDKindID(id, sizeof(id) - 1), metadata);

ponyint_pool_free(alloc_index, params);
}

void handle_branch_prediction_default(LLVMContextRef ctx, LLVMValueRef branch,
ast_t* ast)
{
if(ast_has_annotation(ast, "likely"))
{
unsigned int weights[] =
{PONY_BRANCHWEIGHT_LIKELY, PONY_BRANCHWEIGHT_UNLIKELY};
attach_branchweights_metadata(ctx, branch, weights, 2);
} else if(ast_has_annotation(ast, "unlikely")) {
unsigned int weights[] =
{PONY_BRANCHWEIGHT_UNLIKELY, PONY_BRANCHWEIGHT_LIKELY};
attach_branchweights_metadata(ctx, branch, weights, 2);
}
}
15 changes: 15 additions & 0 deletions src/libponyc/codegen/gencontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ LLVMValueRef gen_try(compile_t* c, ast_t* ast);

LLVMValueRef gen_error(compile_t* c, ast_t* ast);

void attach_branchweights_metadata(LLVMContextRef ctx, LLVMValueRef branch,
unsigned int weights[], unsigned int count);

void handle_branch_prediction_default(LLVMContextRef ctx, LLVMValueRef branch,
ast_t* ast);

// Numbers from Clang's __builtin_expect()
#if PONY_LLVM < 309
# define PONY_BRANCHWEIGHT_LIKELY 64
# define PONY_BRANCHWEIGHT_UNLIKELY 4
#else
# define PONY_BRANCHWEIGHT_LIKELY 2000
# define PONY_BRANCHWEIGHT_UNLIKELY 1
#endif

PONY_EXTERN_C_END

#endif
115 changes: 94 additions & 21 deletions src/libponyc/codegen/genmatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "genoperator.h"
#include "genreference.h"
#include "gencall.h"
#include "gencontrol.h"
#include "../pass/expr.h"
#include "../type/subtype.h"
#include "../type/matchtype.h"
Expand All @@ -12,8 +13,15 @@
#include "../type/lookup.h"
#include <assert.h>

typedef enum
{
WEIGHT_NONE,
WEIGHT_LIKELY,
WEIGHT_UNLIKELY
} match_weight_t;

static bool check_type(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
ast_t* pattern_type, LLVMBasicBlockRef next_block);
ast_t* pattern_type, LLVMBasicBlockRef next_block, match_weight_t weight);

static bool dynamic_match_ptr(compile_t* c, LLVMValueRef ptr,
LLVMValueRef desc, ast_t* pattern, LLVMBasicBlockRef next_block);
Expand All @@ -36,15 +44,38 @@ static ast_t* eq_param_type(compile_t* c, ast_t* pattern)
}

static bool check_nominal(compile_t* c, LLVMValueRef desc, ast_t* pattern_type,
LLVMBasicBlockRef next_block)
LLVMBasicBlockRef next_block, match_weight_t weight)
{
LLVMValueRef test = gendesc_isnominal(c, desc, pattern_type);

if(test == GEN_NOVALUE)
return false;

LLVMBasicBlockRef continue_block = codegen_block(c, "pattern_continue");
LLVMBuildCondBr(c->builder, test, continue_block, next_block);
LLVMValueRef br = LLVMBuildCondBr(c->builder, test, continue_block,
next_block);

switch(weight)
{
case WEIGHT_LIKELY:
{
unsigned int weights[] =
{PONY_BRANCHWEIGHT_LIKELY, PONY_BRANCHWEIGHT_UNLIKELY};
attach_branchweights_metadata(c->context, br, weights, 2);
break;
}

case WEIGHT_UNLIKELY:
{
unsigned int weights[] =
{PONY_BRANCHWEIGHT_UNLIKELY, PONY_BRANCHWEIGHT_LIKELY};
attach_branchweights_metadata(c->context, br, weights, 2);
break;
}

default: {}
}

LLVMPositionBuilderAtEnd(c->builder, continue_block);
return true;
}
Expand All @@ -63,7 +94,7 @@ static void check_cardinality(compile_t* c, LLVMValueRef desc, size_t size,
}

static bool check_tuple(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
ast_t* pattern_type, LLVMBasicBlockRef next_block)
ast_t* pattern_type, LLVMBasicBlockRef next_block, match_weight_t weight)
{
// First check cardinality.
size_t size = ast_childcount(pattern_type);
Expand Down Expand Up @@ -95,15 +126,17 @@ static bool check_tuple(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
LLVMValueRef object_desc = gendesc_fetch(c, object);
object_ptr = gendesc_ptr_to_fields(c, object, object_desc);

if(!check_type(c, object_ptr, object_desc, pattern_child, next_block))
if(!check_type(c, object_ptr, object_desc, pattern_child, next_block,
weight))
return false;

LLVMBuildBr(c->builder, continue_block);

// Continue with the pointer and descriptor.
LLVMPositionBuilderAtEnd(c->builder, nonnull_block);

if(!check_type(c, field_ptr, field_desc, pattern_child, next_block))
if(!check_type(c, field_ptr, field_desc, pattern_child, next_block,
weight))
return false;

LLVMBuildBr(c->builder, continue_block);
Expand All @@ -117,7 +150,7 @@ static bool check_tuple(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
}

static bool check_union(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
ast_t* pattern_type, LLVMBasicBlockRef next_block)
ast_t* pattern_type, LLVMBasicBlockRef next_block, match_weight_t weight)
{
// We have to match some component type.
LLVMBasicBlockRef continue_block = codegen_block(c, "pattern_continue");
Expand All @@ -135,7 +168,7 @@ static bool check_union(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
else
nomatch_block = next_block;

if(!check_type(c, ptr, desc, child, nomatch_block))
if(!check_type(c, ptr, desc, child, nomatch_block, weight))
return false;

// If we do match, jump to the continue block.
Expand All @@ -152,14 +185,14 @@ static bool check_union(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
}

static bool check_isect(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
ast_t* pattern_type, LLVMBasicBlockRef next_block)
ast_t* pattern_type, LLVMBasicBlockRef next_block, match_weight_t weight)
{
// We have to match all component types.
ast_t* child = ast_child(pattern_type);

while(child != NULL)
{
if(!check_type(c, ptr, desc, child, next_block))
if(!check_type(c, ptr, desc, child, next_block, weight))
return false;

child = ast_sibling(child);
Expand All @@ -169,31 +202,31 @@ static bool check_isect(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
}

static bool check_type(compile_t* c, LLVMValueRef ptr, LLVMValueRef desc,
ast_t* pattern_type, LLVMBasicBlockRef next_block)
ast_t* pattern_type, LLVMBasicBlockRef next_block, match_weight_t weight)
{
switch(ast_id(pattern_type))
{
case TK_NOMINAL:
// We are trying to capture the match expression as a nominal.
return check_nominal(c, desc, pattern_type, next_block);
return check_nominal(c, desc, pattern_type, next_block, weight);

case TK_TUPLETYPE:
// We are trying to capture the match expression as a tuple.
return check_tuple(c, ptr, desc, pattern_type, next_block);
return check_tuple(c, ptr, desc, pattern_type, next_block, weight);

case TK_UNIONTYPE:
// We are trying to capture the match expression as a union.
return check_union(c, ptr, desc, pattern_type, next_block);
return check_union(c, ptr, desc, pattern_type, next_block, weight);

case TK_ISECTTYPE:
// We are trying to capture the match expression as an intersection.
return check_isect(c, ptr, desc, pattern_type, next_block);
return check_isect(c, ptr, desc, pattern_type, next_block, weight);

case TK_ARROW:
// We are trying to capture the match expression as a viewpoint type, so
// try again with the right-hand side of the arrow.
return check_type(c, ptr, desc, ast_childidx(pattern_type, 1),
next_block);
next_block, weight);

default: {}
}
Expand All @@ -218,7 +251,11 @@ static bool check_value(compile_t* c, ast_t* pattern, ast_t* param_type,

LLVMBasicBlockRef continue_block = codegen_block(c, "pattern_continue");
LLVMValueRef test = LLVMBuildTrunc(c->builder, result, c->i1, "");
LLVMBuildCondBr(c->builder, test, continue_block, next_block);
LLVMValueRef br = LLVMBuildCondBr(c->builder, test, continue_block,
next_block);

handle_branch_prediction_default(c->context, br, ast_parent(pattern));

LLVMPositionBuilderAtEnd(c->builder, continue_block);
return true;
}
Expand Down Expand Up @@ -311,9 +348,18 @@ static bool dynamic_value_ptr(compile_t* c, LLVMValueRef ptr,
// Get the type of the right-hand side of the pattern's eq() function.
ast_t* param_type = eq_param_type(c, pattern);

ast_t* the_case = ast_parent(pattern);
match_weight_t weight;
if(ast_has_annotation(the_case, "likely"))
weight = WEIGHT_LIKELY;
else if(ast_has_annotation(the_case, "unlikely"))
weight = WEIGHT_UNLIKELY;
else
weight = WEIGHT_NONE;

// Check the runtime type. We pass a pointer to the fields because we may
// still need to match a tuple type inside a type expression.
if(!check_type(c, ptr, desc, param_type, next_block))
if(!check_type(c, ptr, desc, param_type, next_block, weight))
return false;

// We now know that ptr points to something of type pattern_type, and that
Expand All @@ -335,9 +381,18 @@ static bool dynamic_capture_ptr(compile_t* c, LLVMValueRef ptr,
// object, or a nested tuple.
ast_t* pattern_type = ast_type(pattern);

ast_t* the_case = ast_parent(pattern);
match_weight_t weight;
if(ast_has_annotation(the_case, "likely"))
weight = WEIGHT_LIKELY;
else if(ast_has_annotation(the_case, "unlikely"))
weight = WEIGHT_UNLIKELY;
else
weight = WEIGHT_NONE;

// Check the runtime type. We pass a pointer to the fields because we may
// still need to match a tuple type inside a type expression.
if(!check_type(c, ptr, desc, pattern_type, next_block))
if(!check_type(c, ptr, desc, pattern_type, next_block, weight))
return false;

// We now know that ptr points to something of type pattern_type, and that
Expand Down Expand Up @@ -394,9 +449,18 @@ static bool dynamic_value_object(compile_t* c, LLVMValueRef object,
// Build a base pointer that skips the object header.
LLVMValueRef ptr = gendesc_ptr_to_fields(c, object, desc);

ast_t* the_case = ast_parent(pattern);
match_weight_t weight;
if(ast_has_annotation(the_case, "likely"))
weight = WEIGHT_LIKELY;
else if(ast_has_annotation(the_case, "unlikely"))
weight = WEIGHT_UNLIKELY;
else
weight = WEIGHT_NONE;

// Check the runtime type. We pass a pointer to the fields because we may
// still need to match a tuple type inside a type expression.
if(!check_type(c, ptr, desc, param_type, next_block))
if(!check_type(c, ptr, desc, param_type, next_block, weight))
return false;

return check_value(c, pattern, param_type, object, next_block);
Expand All @@ -410,9 +474,18 @@ static bool dynamic_capture_object(compile_t* c, LLVMValueRef object,
// Build a base pointer that skips the object header.
LLVMValueRef ptr = gendesc_ptr_to_fields(c, object, desc);

ast_t* the_case = ast_parent(pattern);
match_weight_t weight;
if(ast_has_annotation(the_case, "likely"))
weight = WEIGHT_LIKELY;
else if(ast_has_annotation(the_case, "unlikely"))
weight = WEIGHT_UNLIKELY;
else
weight = WEIGHT_NONE;

// Check the runtime type. We pass a pointer to the fields because we may
// still need to match a tuple type inside a type expression.
if(!check_type(c, ptr, desc, pattern_type, next_block))
if(!check_type(c, ptr, desc, pattern_type, next_block, weight))
return false;

// As long as the type is correct, we can assign it, with gen_assign_value()
Expand Down

0 comments on commit f9c12da

Please sign in to comment.