-
Notifications
You must be signed in to change notification settings - Fork 63
/
opt-control.hpp
116 lines (102 loc) · 3.59 KB
/
opt-control.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#ifndef UARCH_BENCH_OPT_CONTROL_H_
#define UARCH_BENCH_OPT_CONTROL_H_
#include <type_traits>
#include "hedley.h"
namespace opt_control {
/*
* "sinking" a value instructs the compiler to calculate it, i.e.,
* makes the compiler believe that the value is necessary and hence
* must be calculated.
*
* Note that this function does not imply that the value is *modified*
* which means that a compiler may assume the value is unchanged across
* it. This means that code on either side of the sink that deals with
* the sunk value can still be optimized, as long as the correct value
* is somehow "produced" for each call to sink. If you don't want that,
* you might be looking for modify() instead.
*
* The actual sink implementation is empty and
* so usually leaves no trace in the generated code except that the
* value will be calculated.
*
* Currently, only primitive ("arithmetic") values are accepted, for
* classes and pointers we need to think more about the meaning of
* sinking a value.
*/
template <typename T>
HEDLEY_ALWAYS_INLINE
static void sink(T x) {
static_assert(std::is_arithmetic<T>::value, "types should be primitive");
__asm__ volatile ("" :: "r"(x) :);
}
HEDLEY_ALWAYS_INLINE
static void sink(double x) {
__asm__ volatile ("" :: "x"(x) :);
}
HEDLEY_ALWAYS_INLINE
static void sink(float x) {
__asm__ volatile ("" :: "x"(x) :);
}
/*
* "modifying" a value instructs the compiler to produce the value, and
* also that the value is changed after the call, i.e., that the value cannot
* be cached, precaculated or folded around this call.
*
* The value is NOT actually modified: no instructions are emitted by the asm,
* it only prevents the compiler from assuming the value is unmodified.
*
* This asm block is declared volatile, so the effect above always occurs, even
* if the value is determined to be dead after the call. In particular, the input
* value will be calculated even if the output is never used. Contrast with
* modify_nv, which has the same semantics but is non-volatile.
*/
template <typename T>
HEDLEY_ALWAYS_INLINE
static void modify(T& x) {
static_assert(std::is_arithmetic<T>::value || std::is_pointer<T>::value, "type should be primitive");
__asm__ volatile ("" :"+r"(x)::);
}
/**
* Specialization of modify for double to use the xmm
* register type (x86 specific).
*/
HEDLEY_ALWAYS_INLINE
static void modify(double& x) {
__asm__ volatile (" " :"+x"(x)::);
}
/**
* Specialization of modify for float to use the xmm
* register type (x86 specific).
*/
HEDLEY_ALWAYS_INLINE
static void modify(float& x) {
__asm__ volatile ("" :"+x"(x)::);
}
/*
* "overwriting" a value instructs the compiler that the passed value has been
* overwritten and so the value is unknown after this call (and so is not subject
* to various optimizations).
*
* The value is NOT actually modified: no instructions are emitted by the asm,
* it only prevents the compiler from assuming the value is unmodified.
*
* This function does not use the value as an input, so the compiler may not
* calculate the up-to-date value of the variable at the point of the call, since
* that value is dead (about to be overwritten).
*/
template <typename T>
HEDLEY_ALWAYS_INLINE
static void overwrite(T& x) {
static_assert(std::is_arithmetic<T>::value, "types should be primitive");
__asm__ volatile ("" :"=r"(x)::);
}
/*
* Similar to sink except that it sinks the content pointed to
* by the pointer, so the compiler will materialize in memory
* anything pointed to by the pointer.
*/
static inline void sink_ptr(void *p) {
__asm__ volatile ("" :: "r"(p) : "memory");
}
}
#endif