• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef rr_Nucleus_hpp
16 #define rr_Nucleus_hpp
17 
18 #include <atomic>
19 #include <cassert>
20 #include <cstdarg>
21 #include <cstdint>
22 #include <functional>
23 #include <memory>
24 #include <string>
25 #include <vector>
26 
27 #ifdef None
28 #	undef None  // TODO(b/127920555)
29 #endif
30 
31 static_assert(sizeof(short) == 2, "Reactor's 'Short' type is 16-bit, and requires the C++ 'short' to match that.");
32 static_assert(sizeof(int) == 4, "Reactor's 'Int' type is 32-bit, and requires the C++ 'int' to match that.");
33 
34 namespace rr {
35 
36 class Type;
37 class Value;
38 class SwitchCases;
39 class BasicBlock;
40 class Routine;
41 
42 // Optimization holds the optimization settings for code generation.
43 class Optimization
44 {
45 public:
46 	enum class Level
47 	{
48 		None,
49 		Less,
50 		Default,
51 		Aggressive,
52 	};
53 
54 	enum class Pass
55 	{
56 		Disabled,
57 		InstructionCombining,
58 		CFGSimplification,
59 		LICM,
60 		AggressiveDCE,
61 		GVN,
62 		Reassociate,
63 		DeadStoreElimination,
64 		SCCP,
65 		ScalarReplAggregates,
66 		EarlyCSEPass,
67 
68 		Count,
69 	};
70 
71 	using Passes = std::vector<Pass>;
72 
Optimization(Level level=Level::Default,const Passes & passes={})73 	Optimization(Level level = Level::Default, const Passes &passes = {})
74 	    : level(level)
75 	    , passes(passes)
76 	{
77 #if defined(REACTOR_DEFAULT_OPT_LEVEL)
78 		{
79 			this->level = Level::REACTOR_DEFAULT_OPT_LEVEL;
80 		}
81 #endif
82 	}
83 
getLevel() const84 	Level getLevel() const { return level; }
getPasses() const85 	const Passes &getPasses() const { return passes; }
86 
87 private:
88 	Level level = Level::Default;
89 	Passes passes;
90 };
91 
92 struct DebugConfig
93 {
94 	std::string asmEmitDir = "";
95 };
96 
97 // Config holds the Reactor configuration settings.
98 class Config
99 {
100 public:
101 	// Edit holds a number of modifications to a config, that can be applied
102 	// on an existing Config to produce a new Config with the specified
103 	// changes.
104 	class Edit
105 	{
106 	public:
set(Optimization::Level level)107 		Edit &set(Optimization::Level level)
108 		{
109 			optLevel = level;
110 			optLevelChanged = true;
111 			return *this;
112 		}
add(Optimization::Pass pass)113 		Edit &add(Optimization::Pass pass)
114 		{
115 			optPassEdits.push_back({ ListEdit::Add, pass });
116 			return *this;
117 		}
remove(Optimization::Pass pass)118 		Edit &remove(Optimization::Pass pass)
119 		{
120 			optPassEdits.push_back({ ListEdit::Remove, pass });
121 			return *this;
122 		}
clearOptimizationPasses()123 		Edit &clearOptimizationPasses()
124 		{
125 			optPassEdits.push_back({ ListEdit::Clear, Optimization::Pass::Disabled });
126 			return *this;
127 		}
setDebugConfig(const DebugConfig & cfg)128 		Edit &setDebugConfig(const DebugConfig &cfg)
129 		{
130 			debugCfg = cfg;
131 			debugCfgChanged = true;
132 			return *this;
133 		}
134 
135 		Config apply(const Config &cfg) const;
136 
137 	private:
138 		enum class ListEdit
139 		{
140 			Add,
141 			Remove,
142 			Clear
143 		};
144 		using OptPassesEdit = std::pair<ListEdit, Optimization::Pass>;
145 
146 		template<typename T>
147 		void apply(const std::vector<std::pair<ListEdit, T>> &edits, std::vector<T> &list) const;
148 
149 		Optimization::Level optLevel;
150 		bool optLevelChanged = false;
151 		std::vector<OptPassesEdit> optPassEdits;
152 		DebugConfig debugCfg;
153 		bool debugCfgChanged = false;
154 	};
155 
156 	Config() = default;
Config(const Optimization & optimization,const DebugConfig & debugCfg)157 	Config(const Optimization &optimization, const DebugConfig &debugCfg)
158 	    : optimization(optimization)
159 	    , debugCfg(debugCfg)
160 	{}
161 
getOptimization() const162 	const Optimization &getOptimization() const { return optimization; }
getDebugConfig() const163 	const DebugConfig &getDebugConfig() const { return debugCfg; }
164 
165 private:
166 	Optimization optimization;
167 	DebugConfig debugCfg;
168 };
169 
170 class Nucleus
171 {
172 public:
173 	Nucleus();
174 
175 	virtual ~Nucleus();
176 
177 	// Default configuration to use when no other configuration is specified.
178 	// The new configuration will be applied to subsequent reactor calls.
179 	static void setDefaultConfig(const Config &cfg);
180 	static void adjustDefaultConfig(const Config::Edit &cfgEdit);
181 	static Config getDefaultConfig();
182 
183 	std::shared_ptr<Routine> acquireRoutine(const char *name, const Config::Edit *cfgEdit = nullptr);
184 
185 	static Value *allocateStackVariable(Type *type, int arraySize = 0);
186 	static BasicBlock *createBasicBlock();
187 	static BasicBlock *getInsertBlock();
188 	static void setInsertBlock(BasicBlock *basicBlock);
189 
190 	static void createFunction(Type *returnType, const std::vector<Type *> &paramTypes);
191 	static Value *getArgument(unsigned int index);
192 
193 	// Coroutines
194 	using CoroutineHandle = void *;
195 
196 	template<typename... ARGS>
197 	using CoroutineBegin = CoroutineHandle(ARGS...);
198 	using CoroutineAwait = bool(CoroutineHandle, void *yieldValue);
199 	using CoroutineDestroy = void(CoroutineHandle);
200 
201 	enum CoroutineEntries
202 	{
203 		CoroutineEntryBegin = 0,
204 		CoroutineEntryAwait,
205 		CoroutineEntryDestroy,
206 		CoroutineEntryCount
207 	};
208 
209 	// Begins the generation of the three coroutine functions: CoroutineBegin, CoroutineAwait, and CoroutineDestroy,
210 	// which will be returned by Routine::getEntry() with arg CoroutineEntryBegin, CoroutineEntryAwait, and CoroutineEntryDestroy
211 	// respectively. Called by Coroutine constructor.
212 	// Params are used to generate the params to CoroutineBegin, while ReturnType is used as the YieldType for the coroutine,
213 	// returned via CoroutineAwait..
214 	static void createCoroutine(Type *returnType, const std::vector<Type *> &params);
215 	// Generates code to store the passed in value, and to suspend execution of the coroutine, such that the next call to
216 	// CoroutineAwait can set the output yieldValue and resume execution of the coroutine.
217 	static void yield(Value *val);
218 	// Called to finalize coroutine creation. After this call, Routine::getEntry can be called to retrieve the entry point to any
219 	// of the three coroutine functions. Called by Coroutine::finalize.
220 	std::shared_ptr<Routine> acquireCoroutine(const char *name, const Config::Edit *cfg = nullptr);
221 	// Called by Coroutine::operator() to execute CoroutineEntryBegin wrapped up in func. This is needed in case
222 	// the call must be run on a separate thread of execution (e.g. on a fiber).
223 	static CoroutineHandle invokeCoroutineBegin(Routine &routine, std::function<CoroutineHandle()> func);
224 
225 	// Terminators
226 	static void createRetVoid();
227 	static void createRet(Value *V);
228 	static void createBr(BasicBlock *dest);
229 	static void createCondBr(Value *cond, BasicBlock *ifTrue, BasicBlock *ifFalse);
230 
231 	// Binary operators
232 	static Value *createAdd(Value *lhs, Value *rhs);
233 	static Value *createSub(Value *lhs, Value *rhs);
234 	static Value *createMul(Value *lhs, Value *rhs);
235 	static Value *createUDiv(Value *lhs, Value *rhs);
236 	static Value *createSDiv(Value *lhs, Value *rhs);
237 	static Value *createFAdd(Value *lhs, Value *rhs);
238 	static Value *createFSub(Value *lhs, Value *rhs);
239 	static Value *createFMul(Value *lhs, Value *rhs);
240 	static Value *createFDiv(Value *lhs, Value *rhs);
241 	static Value *createURem(Value *lhs, Value *rhs);
242 	static Value *createSRem(Value *lhs, Value *rhs);
243 	static Value *createFRem(Value *lhs, Value *rhs);
244 	static Value *createShl(Value *lhs, Value *rhs);
245 	static Value *createLShr(Value *lhs, Value *rhs);
246 	static Value *createAShr(Value *lhs, Value *rhs);
247 	static Value *createAnd(Value *lhs, Value *rhs);
248 	static Value *createOr(Value *lhs, Value *rhs);
249 	static Value *createXor(Value *lhs, Value *rhs);
250 
251 	// Unary operators
252 	static Value *createNeg(Value *V);
253 	static Value *createFNeg(Value *V);
254 	static Value *createNot(Value *V);
255 
256 	// Memory instructions
257 	static Value *createLoad(Value *ptr, Type *type, bool isVolatile = false, unsigned int alignment = 0, bool atomic = false, std::memory_order memoryOrder = std::memory_order_relaxed);
258 	static Value *createStore(Value *value, Value *ptr, Type *type, bool isVolatile = false, unsigned int aligment = 0, bool atomic = false, std::memory_order memoryOrder = std::memory_order_relaxed);
259 	static Value *createGEP(Value *ptr, Type *type, Value *index, bool unsignedIndex);
260 
261 	// Masked Load / Store instructions
262 	static Value *createMaskedLoad(Value *base, Type *elementType, Value *mask, unsigned int alignment, bool zeroMaskedLanes);
263 	static void createMaskedStore(Value *base, Value *value, Value *mask, unsigned int alignment);
264 
265 	// Barrier instructions
266 	static void createFence(std::memory_order memoryOrder);
267 
268 	// Atomic instructions
269 	static Value *createAtomicAdd(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
270 	static Value *createAtomicSub(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
271 	static Value *createAtomicAnd(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
272 	static Value *createAtomicOr(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
273 	static Value *createAtomicXor(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
274 	static Value *createAtomicMin(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
275 	static Value *createAtomicMax(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
276 	static Value *createAtomicUMin(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
277 	static Value *createAtomicUMax(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
278 	static Value *createAtomicExchange(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
279 	static Value *createAtomicCompareExchange(Value *ptr, Value *value, Value *compare, std::memory_order memoryOrderEqual, std::memory_order memoryOrderUnequal);
280 
281 	// Cast/Conversion Operators
282 	static Value *createTrunc(Value *V, Type *destType);
283 	static Value *createZExt(Value *V, Type *destType);
284 	static Value *createSExt(Value *V, Type *destType);
285 	static Value *createFPToUI(Value *V, Type *destType);
286 	static Value *createFPToSI(Value *V, Type *destType);
287 	static Value *createSIToFP(Value *V, Type *destType);
288 	static Value *createFPTrunc(Value *V, Type *destType);
289 	static Value *createFPExt(Value *V, Type *destType);
290 	static Value *createBitCast(Value *V, Type *destType);
291 
292 	// Compare instructions
293 	static Value *createICmpEQ(Value *lhs, Value *rhs);
294 	static Value *createICmpNE(Value *lhs, Value *rhs);
295 	static Value *createICmpUGT(Value *lhs, Value *rhs);
296 	static Value *createICmpUGE(Value *lhs, Value *rhs);
297 	static Value *createICmpULT(Value *lhs, Value *rhs);
298 	static Value *createICmpULE(Value *lhs, Value *rhs);
299 	static Value *createICmpSGT(Value *lhs, Value *rhs);
300 	static Value *createICmpSGE(Value *lhs, Value *rhs);
301 	static Value *createICmpSLT(Value *lhs, Value *rhs);
302 	static Value *createICmpSLE(Value *lhs, Value *rhs);
303 	static Value *createFCmpOEQ(Value *lhs, Value *rhs);
304 	static Value *createFCmpOGT(Value *lhs, Value *rhs);
305 	static Value *createFCmpOGE(Value *lhs, Value *rhs);
306 	static Value *createFCmpOLT(Value *lhs, Value *rhs);
307 	static Value *createFCmpOLE(Value *lhs, Value *rhs);
308 	static Value *createFCmpONE(Value *lhs, Value *rhs);
309 	static Value *createFCmpORD(Value *lhs, Value *rhs);
310 	static Value *createFCmpUNO(Value *lhs, Value *rhs);
311 	static Value *createFCmpUEQ(Value *lhs, Value *rhs);
312 	static Value *createFCmpUGT(Value *lhs, Value *rhs);
313 	static Value *createFCmpUGE(Value *lhs, Value *rhs);
314 	static Value *createFCmpULT(Value *lhs, Value *rhs);
315 	static Value *createFCmpULE(Value *lhs, Value *rhs);
316 	static Value *createFCmpUNE(Value *lhs, Value *rhs);
317 
318 	// Vector instructions
319 	static Value *createExtractElement(Value *vector, Type *type, int index);
320 	static Value *createInsertElement(Value *vector, Value *element, int index);
321 	static Value *createShuffleVector(Value *V1, Value *V2, const int *select);
322 
323 	// Other instructions
324 	static Value *createSelect(Value *C, Value *ifTrue, Value *ifFalse);
325 	static SwitchCases *createSwitch(Value *control, BasicBlock *defaultBranch, unsigned numCases);
326 	static void addSwitchCase(SwitchCases *switchCases, int label, BasicBlock *branch);
327 	static void createUnreachable();
328 
329 	// Constant values
330 	static Value *createNullValue(Type *type);
331 	static Value *createConstantLong(int64_t i);
332 	static Value *createConstantInt(int i);
333 	static Value *createConstantInt(unsigned int i);
334 	static Value *createConstantBool(bool b);
335 	static Value *createConstantByte(signed char i);
336 	static Value *createConstantByte(unsigned char i);
337 	static Value *createConstantShort(short i);
338 	static Value *createConstantShort(unsigned short i);
339 	static Value *createConstantFloat(float x);
340 	static Value *createNullPointer(Type *type);
341 	static Value *createConstantVector(const int64_t *constants, Type *type);
342 	static Value *createConstantVector(const double *constants, Type *type);
343 	static Value *createConstantString(const char *v);
createConstantString(const std::string & v)344 	static Value *createConstantString(const std::string &v) { return createConstantString(v.c_str()); }
345 
346 	static Type *getType(Value *value);
347 	static Type *getContainedType(Type *vectorType);
348 	static Type *getPointerType(Type *elementType);
349 	static Type *getPrintfStorageType(Type *valueType);
350 
351 	// Diagnostic utilities
352 	struct OptimizerReport
353 	{
354 		int allocas = 0;
355 		int loads = 0;
356 		int stores = 0;
357 	};
358 
359 	using OptimizerCallback = void(const OptimizerReport *report);
360 
361 	// Sets the callback to be used by the next optimizer invocation (during acquireRoutine),
362 	// for reporting stats about the resulting IR code. For testing only.
363 	static void setOptimizerCallback(OptimizerCallback *callback);
364 };
365 
366 }  // namespace rr
367 
368 #endif  // rr_Nucleus_hpp
369