//===----- TypePromotion.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // /// \file /// This is an opcode based type promotion pass for small types that would /// otherwise be promoted during legalisation. This works around the limitations /// of selection dag for cyclic regions. The search begins from icmp /// instructions operands where a tree, consisting of non-wrapping or safe /// wrapping instructions, is built, checked and promoted if possible. /// //===----------------------------------------------------------------------===// #include "llvm/CodeGen/TypePromotion.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Target/TargetMachine.h" #define DEBUG_TYPE … #define PASS_NAME … usingnamespacellvm; static cl::opt<bool> DisablePromotion("disable-type-promotion", cl::Hidden, cl::init(false), cl::desc("Disable type promotion pass")); // The goal of this pass is to enable more efficient code generation for // operations on narrow types (i.e. types with < 32-bits) and this is a // motivating IR code example: // // define hidden i32 @cmp(i8 zeroext) { // %2 = add i8 %0, -49 // %3 = icmp ult i8 %2, 3 // .. // } // // The issue here is that i8 is type-legalized to i32 because i8 is not a // legal type. Thus, arithmetic is done in integer-precision, but then the // byte value is masked out as follows: // // t19: i32 = add t4, Constant:i32<-49> // t24: i32 = and t19, Constant:i32<255> // // Consequently, we generate code like this: // // subs r0, #49 // uxtb r1, r0 // cmp r1, #3 // // This shows that masking out the byte value results in generation of // the UXTB instruction. This is not optimal as r0 already contains the byte // value we need, and so instead we can just generate: // // sub.w r1, r0, #49 // cmp r1, #3 // // We achieve this by type promoting the IR to i32 like so for this example: // // define i32 @cmp(i8 zeroext %c) { // %0 = zext i8 %c to i32 // %c.off = add i32 %0, -49 // %1 = icmp ult i32 %c.off, 3 // .. // } // // For this to be valid and legal, we need to prove that the i32 add is // producing the same value as the i8 addition, and that e.g. no overflow // happens. // // A brief sketch of the algorithm and some terminology. // We pattern match interesting IR patterns: // - which have "sources": instructions producing narrow values (i8, i16), and // - they have "sinks": instructions consuming these narrow values. // // We collect all instruction connecting sources and sinks in a worklist, so // that we can mutate these instruction and perform type promotion when it is // legal to do so. namespace { class IRPromoter { … }; class TypePromotionImpl { … }; class TypePromotionLegacy : public FunctionPass { … }; } // namespace static bool GenerateSignBits(Instruction *I) { … } bool TypePromotionImpl::EqualTypeSize(Value *V) { … } bool TypePromotionImpl::LessOrEqualTypeSize(Value *V) { … } bool TypePromotionImpl::GreaterThanTypeSize(Value *V) { … } bool TypePromotionImpl::LessThanTypeSize(Value *V) { … } /// Return true if the given value is a source in the use-def chain, producing /// a narrow 'TypeSize' value. These values will be zext to start the promotion /// of the tree to i32. We guarantee that these won't populate the upper bits /// of the register. ZExt on the loads will be free, and the same for call /// return values because we only accept ones that guarantee a zeroext ret val. /// Many arguments will have the zeroext attribute too, so those would be free /// too. bool TypePromotionImpl::isSource(Value *V) { … } /// Return true if V will require any promoted values to be truncated for the /// the IR to remain valid. We can't mutate the value type of these /// instructions. bool TypePromotionImpl::isSink(Value *V) { … } /// Return whether this instruction can safely wrap. bool TypePromotionImpl::isSafeWrap(Instruction *I) { … } bool TypePromotionImpl::shouldPromote(Value *V) { … } /// Return whether we can safely mutate V's type to ExtTy without having to be /// concerned with zero extending or truncation. static bool isPromotedResultSafe(Instruction *I) { … } void IRPromoter::ReplaceAllUsersOfWith(Value *From, Value *To) { … } void IRPromoter::ExtendSources() { … } void IRPromoter::PromoteTree() { … } void IRPromoter::TruncateSinks() { … } void IRPromoter::Cleanup() { … } void IRPromoter::ConvertTruncs() { … } void IRPromoter::Mutate() { … } /// We disallow booleans to make life easier when dealing with icmps but allow /// any other integer that fits in a scalar register. Void types are accepted /// so we can handle switches. bool TypePromotionImpl::isSupportedType(Value *V) { … } /// We accept most instructions, as well as Arguments and ConstantInsts. We /// Disallow casts other than zext and truncs and only allow calls if their /// return value is zeroext. We don't allow opcodes that can introduce sign /// bits. bool TypePromotionImpl::isSupportedValue(Value *V) { … } /// Check that the type of V would be promoted and that the original type is /// smaller than the targeted promoted type. Check that we're not trying to /// promote something larger than our base 'TypeSize' type. bool TypePromotionImpl::isLegalToPromote(Value *V) { … } bool TypePromotionImpl::TryToPromote(Value *V, unsigned PromotedWidth, const LoopInfo &LI) { … } bool TypePromotionImpl::run(Function &F, const TargetMachine *TM, const TargetTransformInfo &TTI, const LoopInfo &LI) { … } INITIALIZE_PASS_BEGIN(TypePromotionLegacy, DEBUG_TYPE, PASS_NAME, false, false) INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) INITIALIZE_PASS_END(TypePromotionLegacy, DEBUG_TYPE, PASS_NAME, false, false) char TypePromotionLegacy::ID = …; bool TypePromotionLegacy::runOnFunction(Function &F) { … } FunctionPass *llvm::createTypePromotionLegacyPass() { … } PreservedAnalyses TypePromotionPass::run(Function &F, FunctionAnalysisManager &AM) { … }