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 *> ¶mTypes); 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 *> ¶ms); 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