Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license 2 : : 3 : : // Lower intrinsics that expose subtarget information to the language. This makes it 4 : : // possible to write code that changes behavior based on, e.g., the availability of 5 : : // specific CPU features. 6 : : // 7 : : // The following intrinsics are supported: 8 : : // - julia.cpu.have_fma.$typ: returns 1 if the platform supports hardware-accelerated FMA. 9 : : // 10 : : // Some of these intrinsics are overloaded, i.e., they are suffixed with a type name. 11 : : // To extend support, make sure codegen (in intrinsics.cpp) knows how to emit them. 12 : : // 13 : : // XXX: can / do we want to make this a codegen pass to enable querying TargetPassConfig 14 : : // instead of using the global target machine? 15 : : 16 : : #include "llvm-version.h" 17 : : #include "passes.h" 18 : : 19 : : #include <llvm/ADT/Statistic.h> 20 : : #include <llvm/IR/Module.h> 21 : : #include <llvm/IR/Constants.h> 22 : : #include <llvm/IR/Instructions.h> 23 : : #include <llvm/IR/PassManager.h> 24 : : #include <llvm/IR/LegacyPassManager.h> 25 : : #include <llvm/IR/Verifier.h> 26 : : #include <llvm/Target/TargetMachine.h> 27 : : #include <llvm/Support/Debug.h> 28 : : 29 : : #include "julia.h" 30 : : #include "jitlayers.h" 31 : : 32 : : #define DEBUG_TYPE "cpufeatures" 33 : : 34 : : using namespace llvm; 35 : : 36 : : STATISTIC(LoweredWithFMA, "Number of have_fma's that were lowered to true"); 37 : : STATISTIC(LoweredWithoutFMA, "Number of have_fma's that were lowered to false"); 38 : : 39 : : extern JuliaOJIT *jl_ExecutionEngine; 40 : : 41 : : // whether this platform unconditionally (i.e. without needing multiversioning) supports FMA 42 : 495 : Optional<bool> always_have_fma(Function &intr) { 43 : 495 : auto intr_name = intr.getName(); 44 : 495 : auto typ = intr_name.substr(strlen("julia.cpu.have_fma.")); 45 : : 46 : : #if defined(_CPU_AARCH64_) 47 : : return typ == "f32" || typ == "f64"; 48 : : #else 49 : : (void)typ; 50 : 495 : return {}; 51 : : #endif 52 : : } 53 : : 54 : 473 : bool have_fma(Function &intr, Function &caller) { 55 : 473 : auto unconditional = always_have_fma(intr); 56 [ - + ]: 473 : if (unconditional.hasValue()) 57 : 0 : return unconditional.getValue(); 58 : : 59 : 473 : auto intr_name = intr.getName(); 60 : 473 : auto typ = intr_name.substr(strlen("julia.cpu.have_fma.")); 61 : : 62 : 473 : Attribute FSAttr = caller.getFnAttribute("target-features"); 63 : : StringRef FS = 64 [ + + ]: 473 : FSAttr.isValid() ? FSAttr.getValueAsString() : jl_ExecutionEngine->getTargetFeatureString(); 65 : : 66 : 946 : SmallVector<StringRef, 6> Features; 67 : 473 : FS.split(Features, ','); 68 [ + - ]: 9933 : for (StringRef Feature : Features) 69 : : #if defined _CPU_ARM_ 70 : : if (Feature == "+vfp4") 71 : : return typ == "f32" || typ == "f64"; 72 : : else if (Feature == "+vfp4sp") 73 : : return typ == "f32"; 74 : : #else 75 [ + + - + : 9933 : if (Feature == "+fma" || Feature == "+fma4") + + ] 76 [ + + + - ]: 473 : return typ == "f32" || typ == "f64"; 77 : : #endif 78 : : 79 : 0 : return false; 80 : : } 81 : : 82 : 473 : void lowerHaveFMA(Function &intr, Function &caller, CallInst *I) { 83 [ + - ]: 473 : if (have_fma(intr, caller)) { 84 : 473 : ++LoweredWithFMA; 85 : 473 : I->replaceAllUsesWith(ConstantInt::get(I->getType(), 1)); 86 : : } else { 87 : 0 : ++LoweredWithoutFMA; 88 : 0 : I->replaceAllUsesWith(ConstantInt::get(I->getType(), 0)); 89 : : } 90 : 473 : return; 91 : : } 92 : : 93 : 334464 : bool lowerCPUFeatures(Module &M) 94 : : { 95 : 668928 : SmallVector<Instruction*,6> Materialized; 96 : : 97 [ + + ]: 5838040 : for (auto &F: M.functions()) { 98 : 5503570 : auto FN = F.getName(); 99 : : 100 [ + + ]: 5503570 : if (FN.startswith("julia.cpu.have_fma.")) { 101 [ + + ]: 594 : for (Use &U: F.uses()) { 102 : 473 : User *RU = U.getUser(); 103 : 473 : CallInst *I = cast<CallInst>(RU); 104 : 473 : lowerHaveFMA(F, *I->getParent()->getParent(), I); 105 : 473 : Materialized.push_back(I); 106 : : } 107 : : } 108 : : } 109 : : 110 [ + + ]: 334464 : if (!Materialized.empty()) { 111 [ + + ]: 594 : for (auto I: Materialized) { 112 : 473 : I->eraseFromParent(); 113 : : } 114 : : assert(!verifyModule(M)); 115 : 121 : return true; 116 : : } else { 117 : 334343 : return false; 118 : : } 119 : : } 120 : : 121 : 0 : PreservedAnalyses CPUFeatures::run(Module &M, ModuleAnalysisManager &AM) 122 : : { 123 [ # # ]: 0 : if (lowerCPUFeatures(M)) { 124 : 0 : return PreservedAnalyses::allInSet<CFGAnalyses>(); 125 : : } 126 : 0 : return PreservedAnalyses::all(); 127 : : } 128 : : 129 : : namespace { 130 : : struct CPUFeaturesLegacy : public ModulePass { 131 : : static char ID; 132 : 1052 : CPUFeaturesLegacy() : ModulePass(ID) {}; 133 : : 134 : 334464 : bool runOnModule(Module &M) 135 : : { 136 : 334464 : return lowerCPUFeatures(M); 137 : : } 138 : : }; 139 : : 140 : : char CPUFeaturesLegacy::ID = 0; 141 : : static RegisterPass<CPUFeaturesLegacy> 142 : : Y("CPUFeatures", 143 : : "Lower calls to CPU feature testing intrinsics.", 144 : : false, 145 : : false); 146 : : } 147 : : 148 : 1052 : Pass *createCPUFeaturesPass() 149 : : { 150 : 1052 : return new CPUFeaturesLegacy(); 151 : : } 152 : : 153 : 0 : extern "C" JL_DLLEXPORT void LLVMExtraAddCPUFeaturesPass_impl(LLVMPassManagerRef PM) 154 : : { 155 : 0 : unwrap(PM)->add(createCPUFeaturesPass()); 156 : 0 : }