OpenTTD
cpu.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include "core/bitmath_func.hpp"
12 
13 #include "safeguards.h"
14 
15 #undef RDTSC_AVAILABLE
16 
17 /* rdtsc for MSC_VER, uses simple inline assembly, or _rdtsc
18  * from external win64.asm because VS2005 does not support inline assembly */
19 #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) && !defined(RDTSC_AVAILABLE)
20 #include <intrin.h>
21 uint64 ottd_rdtsc()
22 {
23  return __rdtsc();
24 }
25 #define RDTSC_AVAILABLE
26 #endif
27 
28 /* rdtsc for OS/2. Hopefully this works, who knows */
29 #if defined (__WATCOMC__) && !defined(RDTSC_AVAILABLE)
30 unsigned __int64 ottd_rdtsc();
31 # pragma aux ottd_rdtsc = 0x0F 0x31 value [edx eax] parm nomemory modify exact [edx eax] nomemory;
32 # define RDTSC_AVAILABLE
33 #endif
34 
35 /* rdtsc for all other *nix-en (hopefully). Use GCC syntax */
36 #if (defined(__i386__) || defined(__x86_64__)) && !defined(RDTSC_AVAILABLE)
37 uint64 ottd_rdtsc()
38 {
39  uint32 high, low;
40  __asm__ __volatile__ ("rdtsc" : "=a" (low), "=d" (high));
41  return ((uint64)high << 32) | low;
42 }
43 # define RDTSC_AVAILABLE
44 #endif
45 
46 /* rdtsc for PPC which has this not */
47 #if (defined(__POWERPC__) || defined(__powerpc__)) && !defined(RDTSC_AVAILABLE)
48 uint64 ottd_rdtsc()
49 {
50  uint32 high = 0, high2 = 0, low;
51  /* PPC does not have rdtsc, so we cheat by reading the two 32-bit time-counters
52  * it has, 'Move From Time Base (Upper)'. Since these are two reads, in the
53  * very unlikely event that the lower part overflows to the upper part while we
54  * read it; we double-check and reread the registers */
55  asm volatile (
56  "mftbu %0\n"
57  "mftb %1\n"
58  "mftbu %2\n"
59  "cmpw %3,%4\n"
60  "bne- $-16\n"
61  : "=r" (high), "=r" (low), "=r" (high2)
62  : "0" (high), "2" (high2)
63  );
64  return ((uint64)high << 32) | low;
65 }
66 # define RDTSC_AVAILABLE
67 #endif
68 
69 /* In all other cases we have no support for rdtsc. No major issue,
70  * you just won't be able to profile your code with TIC()/TOC() */
71 #if !defined(RDTSC_AVAILABLE)
72 #warning "(non-fatal) No support for rdtsc(), you won't be able to profile with TIC/TOC"
73 uint64 ottd_rdtsc() {return 0;}
74 #endif
75 
76 
88 #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
89 void ottd_cpuid(int info[4], int type)
90 {
91  __cpuid(info, type);
92 }
93 #elif defined(__x86_64__) || defined(__i386)
94 void ottd_cpuid(int info[4], int type)
95 {
96 #if defined(__i386) && defined(__PIC__)
97  /* The easy variant would be just cpuid, however... ebx is being used by the GOT (Global Offset Table)
98  * in case of PIC;
99  * clobbering ebx is no alternative: some compiler versions don't like this
100  * and will issue an error message like
101  * "can't find a register in class 'BREG' while reloading 'asm'"
102  */
103  __asm__ __volatile__ (
104  "xchgl %%ebx, %1 \n\t"
105  "cpuid \n\t"
106  "xchgl %%ebx, %1 \n\t"
107  : "=a" (info[0]), "=r" (info[1]), "=c" (info[2]), "=d" (info[3])
108  /* It is safe to write "=r" for (info[1]) as in case that PIC is enabled for i386,
109  * the compiler will not choose EBX as target register (but something else).
110  */
111  : "a" (type)
112  );
113 #else
114  __asm__ __volatile__ (
115  "cpuid \n\t"
116  : "=a" (info[0]), "=b" (info[1]), "=c" (info[2]), "=d" (info[3])
117  : "a" (type)
118  );
119 #endif /* i386 PIC */
120 }
121 #else
122 void ottd_cpuid(int info[4], int type)
123 {
124  info[0] = info[1] = info[2] = info[3] = 0;
125 }
126 #endif
127 
128 bool HasCPUIDFlag(uint type, uint index, uint bit)
129 {
130  int cpu_info[4] = {-1};
131  ottd_cpuid(cpu_info, 0);
132  uint max_info_type = cpu_info[0];
133  if (max_info_type < type) return false;
134 
135  ottd_cpuid(cpu_info, type);
136  return HasBit(cpu_info[index], bit);
137 }
Functions related to bit mathematics.
Definition of base types and functions in a cross-platform compatible way.
A number of safeguards to prevent using unsafe methods.
bool HasCPUIDFlag(uint type, uint index, uint bit)
Check whether the current CPU has the given flag.
Definition: cpu.cpp:128
void ottd_cpuid(int info[4], int type)
Definitions for CPU detection:
Definition: cpu.cpp:122
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
uint64 ottd_rdtsc()
Get the tick counter from the CPU (high precision timing).
Definition: cpu.cpp:73