Info
-
Did you know about [[gnu::cold]] function attribute to mark functions which are unlikely to be called?
Example
void report_error();
[[gnu::cold]] void report_error_cold();
void logic();
/*
cold(bool):
test edi, edi
jne .LBB0_1
jmp logic()
.LBB0_1:
jmp report_error_cold()
*/
auto cold(bool error) {
if (error) {
report_error_cold();
return;
}
logic();
}
/*
normal(bool):
test edi, edi
je .LBB1_2
jmp report_error()
.LBB1_2:
jmp logic()
*/
auto normal(bool error) {
if (error) {
report_error();
return;
}
logic();
}
/*
unlikely(bool):
test edi, edi
jne .LBB2_1
jmp logic()
.LBB2_1:
jmp report_error()
*/
auto unlikely(bool error) {
if (error) [[unlikely]] {
report_error();
return;
}
logic();
}
Puzzle
- Can you implement required error policies using provided lambdas so that they will match assembly?
void logic();
template<auto ErrorPolicy>
auto handle(bool error) {
if (error) {
ErrorPolicy(error);
return;
}
logic();
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) {}>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) { if (error) throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) { }>(error);
}
Solutions
auto test_lambda_cold(bool error) {
handle<[][[gnu::cold]](bool error) {}>(error);
}
auto test_lambda_throw(bool error) {
handle<[](bool error) { if (error) throw; }>(error);
}
auto test_lambda_abort(bool error) {
handle<[][[gnu::hot]] (bool error) { if (error) abort(); }>(error);
}
void report_error();
[[gnu::cold]] void report_error_cold();
void logic();
/*
cold(bool):
test edi, edi
jne .LBB0_1
jmp logic()
.LBB0_1:
jmp report_error_cold()
*/
auto cold(bool error) {
if (error) {
report_error_cold();
return;
}
logic();
}
/*
normal(bool):
test edi, edi
je .LBB1_2
jmp report_error()
.LBB1_2:
jmp logic()
*/
auto normal(bool error) {
if (error) {
report_error();
return;
}
logic();
}
/*
unlikely(bool):
test edi, edi
jne .LBB2_1
jmp logic()
.LBB2_1:
jmp report_error()
*/
auto unlikely(bool error) {
if (error) [[unlikely]] {
report_error();
return;
}
logic();
}
Puzzle
- Can you implement required error policies using provided lambdas so that they will match assembly?
void logic();
template<auto ErrorPolicy>
auto handle(bool error) {
if (error) {
ErrorPolicy(error);
return;
}
logic();
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) {}>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) { if (error) throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) { }>(error);
}
Solutions
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
// adding the [[gnu::cold]] attribute to the lambda changes je to jne
handle<[] [[gnu::cold]] (bool) {}>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
// no attribute needed here: does the throw already cause coldness?
// consider: an if branch that throws is already considered [[unlikely]]
handle<[] (bool) { throw; }>(error);
}
#include <cstdlib>
/*
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
// does abort also imply coldness?
// [[noreturn]] makes no difference here but may also imply coldness?
handle<[] [[noreturn]] (bool) { std::abort(); }>(error);
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
handle<[](bool error) {[[gnu::cold]]; }>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
handle<[](bool error) { if (error) throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
handle<[](bool error) { abort(); }>(error);
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
handle<[] [[gnu::cold]] (bool error) {}>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
handle<[](bool error) { if (error) throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
handle<[](bool error) { std::abort(); }>(error);
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
handle<[] [[gnu::cold]] {}>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L6
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L6:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
handle<[] [[gnu::cold]] { throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L11
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L11:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
handle<[] [[gnu::cold]] { std::abort(); }>(error);
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
// TODO - modify the following line to get the above assembly
handle<[] [[gnu::cold]] (bool error) { }>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool) { throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) { std::abort(); }>(error);
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
// TODO - modify the following line to get the above assembly
handle<[] [[gnu::cold]] (bool error) {}>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
// TODO - modify the following line to get the above assembly
handle<[] (bool error) { throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
// TODO - modify the following line to get the above assembly
handle<[] [[gnu::cold]] (bool error) { abort(); }>(error);
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
// TODO - modify the following line to get the above assembly
handle< [][[gnu::cold]](bool error) {}> (error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
// TODO - modify the following line to get the above assembly
handle<[] (bool error) { if (error) [[unlikely]] throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
// TODO - modify the following line to get the above assembly
handle<[] (bool error) { if(error) [[unlikely]] abort(); }>( error);
}
/*
test_lambda_cold(bool):
test dil, dil
jne .L1
jmp logic()
.L1:
ret
*/
auto test_lambda_cold(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error ) { [[unlikely]] return;}>(error);
}
/*
test_lambda_throw(bool):
test dil, dil
jne .L7
jmp logic()
test_lambda_throw(bool) [clone .cold]:
.L7:
push rax
call __cxa_rethrow
*/
auto test_lambda_throw(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) { if (error)[[unlikely]] throw; }>(error);
}
#include <cstdlib>
/**
test_lambda_abort(bool):
test dil, dil
jne .L12
jmp logic()
test_lambda_abort(bool) [clone .cold]:
.L12:
push rax
call abort
*/
auto test_lambda_abort(bool error) {
// TODO - modify the following line to get the above assembly
handle<[](bool error) { if (error)[[unlikely]] abort(); }>(error) ;
}