Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/NumericalStabilitySanitizer.cpp
35266 views
//===-- NumericalStabilitySanitizer.cpp -----------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file contains the instrumentation pass for the numerical sanitizer.9// Conceptually the pass injects shadow computations using higher precision10// types and inserts consistency checks. For details see the paper11// https://arxiv.org/abs/2102.12782.12//13//===----------------------------------------------------------------------===//1415#include "llvm/Transforms/Instrumentation/NumericalStabilitySanitizer.h"1617#include "llvm/ADT/DenseMap.h"18#include "llvm/ADT/SmallString.h"19#include "llvm/ADT/SmallVector.h"20#include "llvm/ADT/Statistic.h"21#include "llvm/ADT/StringExtras.h"22#include "llvm/Analysis/TargetLibraryInfo.h"23#include "llvm/Analysis/ValueTracking.h"24#include "llvm/IR/DataLayout.h"25#include "llvm/IR/Function.h"26#include "llvm/IR/IRBuilder.h"27#include "llvm/IR/IntrinsicInst.h"28#include "llvm/IR/Intrinsics.h"29#include "llvm/IR/LLVMContext.h"30#include "llvm/IR/MDBuilder.h"31#include "llvm/IR/Metadata.h"32#include "llvm/IR/Module.h"33#include "llvm/IR/Type.h"34#include "llvm/InitializePasses.h"35#include "llvm/Support/CommandLine.h"36#include "llvm/Support/Debug.h"37#include "llvm/Support/MathExtras.h"38#include "llvm/Support/Regex.h"39#include "llvm/Support/raw_ostream.h"40#include "llvm/Transforms/Instrumentation.h"41#include "llvm/Transforms/Utils/BasicBlockUtils.h"42#include "llvm/Transforms/Utils/EscapeEnumerator.h"43#include "llvm/Transforms/Utils/Local.h"44#include "llvm/Transforms/Utils/ModuleUtils.h"4546#include <cstdint>4748using namespace llvm;4950#define DEBUG_TYPE "nsan"5152STATISTIC(NumInstrumentedFTLoads,53"Number of instrumented floating-point loads");5455STATISTIC(NumInstrumentedFTCalls,56"Number of instrumented floating-point calls");57STATISTIC(NumInstrumentedFTRets,58"Number of instrumented floating-point returns");59STATISTIC(NumInstrumentedFTStores,60"Number of instrumented floating-point stores");61STATISTIC(NumInstrumentedNonFTStores,62"Number of instrumented non floating-point stores");63STATISTIC(64NumInstrumentedNonFTMemcpyStores,65"Number of instrumented non floating-point stores with memcpy semantics");66STATISTIC(NumInstrumentedFCmp, "Number of instrumented fcmps");6768// Using smaller shadow types types can help improve speed. For example, `dlq`69// is 3x slower to 5x faster in opt mode and 2-6x faster in dbg mode compared to70// `dqq`.71static cl::opt<std::string> ClShadowMapping(72"nsan-shadow-type-mapping", cl::init("dqq"),73cl::desc("One shadow type id for each of `float`, `double`, `long double`. "74"`d`,`l`,`q`,`e` mean double, x86_fp80, fp128 (quad) and "75"ppc_fp128 (extended double) respectively. The default is to "76"shadow `float` as `double`, and `double` and `x86_fp80` as "77"`fp128`"),78cl::Hidden);7980static cl::opt<bool>81ClInstrumentFCmp("nsan-instrument-fcmp", cl::init(true),82cl::desc("Instrument floating-point comparisons"),83cl::Hidden);8485static cl::opt<std::string> ClCheckFunctionsFilter(86"check-functions-filter",87cl::desc("Only emit checks for arguments of functions "88"whose names match the given regular expression"),89cl::value_desc("regex"));9091static cl::opt<bool> ClTruncateFCmpEq(92"nsan-truncate-fcmp-eq", cl::init(true),93cl::desc(94"This flag controls the behaviour of fcmp equality comparisons."95"For equality comparisons such as `x == 0.0f`, we can perform the "96"shadow check in the shadow (`x_shadow == 0.0) == (x == 0.0f)`) or app "97" domain (`(trunc(x_shadow) == 0.0f) == (x == 0.0f)`). This helps "98"catch the case when `x_shadow` is accurate enough (and therefore "99"close enough to zero) so that `trunc(x_shadow)` is zero even though "100"both `x` and `x_shadow` are not"),101cl::Hidden);102103// When there is external, uninstrumented code writing to memory, the shadow104// memory can get out of sync with the application memory. Enabling this flag105// emits consistency checks for loads to catch this situation.106// When everything is instrumented, this is not strictly necessary because any107// load should have a corresponding store, but can help debug cases when the108// framework did a bad job at tracking shadow memory modifications by failing on109// load rather than store.110// TODO: provide a way to resume computations from the FT value when the load111// is inconsistent. This ensures that further computations are not polluted.112static cl::opt<bool> ClCheckLoads("nsan-check-loads",113cl::desc("Check floating-point load"),114cl::Hidden);115116static cl::opt<bool> ClCheckStores("nsan-check-stores", cl::init(true),117cl::desc("Check floating-point stores"),118cl::Hidden);119120static cl::opt<bool> ClCheckRet("nsan-check-ret", cl::init(true),121cl::desc("Check floating-point return values"),122cl::Hidden);123124// LLVM may store constant floats as bitcasted ints.125// It's not really necessary to shadow such stores,126// if the shadow value is unknown the framework will re-extend it on load127// anyway. Moreover, because of size collisions (e.g. bf16 vs f16) it is128// impossible to determine the floating-point type based on the size.129// However, for debugging purposes it can be useful to model such stores.130static cl::opt<bool> ClPropagateNonFTConstStoresAsFT(131"nsan-propagate-non-ft-const-stores-as-ft",132cl::desc(133"Propagate non floating-point const stores as floating point values."134"For debugging purposes only"),135cl::Hidden);136137constexpr StringLiteral kNsanModuleCtorName("nsan.module_ctor");138constexpr StringLiteral kNsanInitName("__nsan_init");139140// The following values must be kept in sync with the runtime.141constexpr int kShadowScale = 2;142constexpr int kMaxVectorWidth = 8;143constexpr int kMaxNumArgs = 128;144constexpr int kMaxShadowTypeSizeBytes = 16; // fp128145146namespace {147148// Defines the characteristics (type id, type, and floating-point semantics)149// attached for all possible shadow types.150class ShadowTypeConfig {151public:152static std::unique_ptr<ShadowTypeConfig> fromNsanTypeId(char TypeId);153154// The LLVM Type corresponding to the shadow type.155virtual Type *getType(LLVMContext &Context) const = 0;156157// The nsan type id of the shadow type (`d`, `l`, `q`, ...).158virtual char getNsanTypeId() const = 0;159160virtual ~ShadowTypeConfig() = default;161};162163template <char NsanTypeId>164class ShadowTypeConfigImpl : public ShadowTypeConfig {165public:166char getNsanTypeId() const override { return NsanTypeId; }167static constexpr const char kNsanTypeId = NsanTypeId;168};169170// `double` (`d`) shadow type.171class F64ShadowConfig : public ShadowTypeConfigImpl<'d'> {172Type *getType(LLVMContext &Context) const override {173return Type::getDoubleTy(Context);174}175};176177// `x86_fp80` (`l`) shadow type: X86 long double.178class F80ShadowConfig : public ShadowTypeConfigImpl<'l'> {179Type *getType(LLVMContext &Context) const override {180return Type::getX86_FP80Ty(Context);181}182};183184// `fp128` (`q`) shadow type.185class F128ShadowConfig : public ShadowTypeConfigImpl<'q'> {186Type *getType(LLVMContext &Context) const override {187return Type::getFP128Ty(Context);188}189};190191// `ppc_fp128` (`e`) shadow type: IBM extended double with 106 bits of mantissa.192class PPC128ShadowConfig : public ShadowTypeConfigImpl<'e'> {193Type *getType(LLVMContext &Context) const override {194return Type::getPPC_FP128Ty(Context);195}196};197198// Creates a ShadowTypeConfig given its type id.199std::unique_ptr<ShadowTypeConfig>200ShadowTypeConfig::fromNsanTypeId(const char TypeId) {201switch (TypeId) {202case F64ShadowConfig::kNsanTypeId:203return std::make_unique<F64ShadowConfig>();204case F80ShadowConfig::kNsanTypeId:205return std::make_unique<F80ShadowConfig>();206case F128ShadowConfig::kNsanTypeId:207return std::make_unique<F128ShadowConfig>();208case PPC128ShadowConfig::kNsanTypeId:209return std::make_unique<PPC128ShadowConfig>();210}211report_fatal_error("nsan: invalid shadow type id '" + Twine(TypeId) + "'");212}213214// An enum corresponding to shadow value types. Used as indices in arrays, so215// not an `enum class`.216enum FTValueType { kFloat, kDouble, kLongDouble, kNumValueTypes };217218// If `FT` corresponds to a primitive FTValueType, return it.219static std::optional<FTValueType> ftValueTypeFromType(Type *FT) {220if (FT->isFloatTy())221return kFloat;222if (FT->isDoubleTy())223return kDouble;224if (FT->isX86_FP80Ty())225return kLongDouble;226return {};227}228229// Returns the LLVM type for an FTValueType.230static Type *typeFromFTValueType(FTValueType VT, LLVMContext &Context) {231switch (VT) {232case kFloat:233return Type::getFloatTy(Context);234case kDouble:235return Type::getDoubleTy(Context);236case kLongDouble:237return Type::getX86_FP80Ty(Context);238case kNumValueTypes:239return nullptr;240}241llvm_unreachable("Unhandled FTValueType enum");242}243244// Returns the type name for an FTValueType.245static const char *typeNameFromFTValueType(FTValueType VT) {246switch (VT) {247case kFloat:248return "float";249case kDouble:250return "double";251case kLongDouble:252return "longdouble";253case kNumValueTypes:254return nullptr;255}256llvm_unreachable("Unhandled FTValueType enum");257}258259// A specific mapping configuration of application type to shadow type for nsan260// (see -nsan-shadow-mapping flag).261class MappingConfig {262public:263explicit MappingConfig(LLVMContext &C) : Context(C) {264if (ClShadowMapping.size() != 3)265report_fatal_error("Invalid nsan mapping: " + Twine(ClShadowMapping));266unsigned ShadowTypeSizeBits[kNumValueTypes];267for (int VT = 0; VT < kNumValueTypes; ++VT) {268auto Config = ShadowTypeConfig::fromNsanTypeId(ClShadowMapping[VT]);269if (!Config)270report_fatal_error("Failed to get ShadowTypeConfig for " +271Twine(ClShadowMapping[VT]));272const unsigned AppTypeSize =273typeFromFTValueType(static_cast<FTValueType>(VT), Context)274->getScalarSizeInBits();275const unsigned ShadowTypeSize =276Config->getType(Context)->getScalarSizeInBits();277// Check that the shadow type size is at most kShadowScale times the278// application type size, so that shadow memory compoutations are valid.279if (ShadowTypeSize > kShadowScale * AppTypeSize)280report_fatal_error("Invalid nsan mapping f" + Twine(AppTypeSize) +281"->f" + Twine(ShadowTypeSize) +282": The shadow type size should be at most " +283Twine(kShadowScale) +284" times the application type size");285ShadowTypeSizeBits[VT] = ShadowTypeSize;286Configs[VT] = std::move(Config);287}288289// Check that the mapping is monotonous. This is required because if one290// does an fpextend of `float->long double` in application code, nsan is291// going to do an fpextend of `shadow(float) -> shadow(long double)` in292// shadow code. This will fail in `qql` mode, since nsan would be293// fpextending `f128->long`, which is invalid.294// TODO: Relax this.295if (ShadowTypeSizeBits[kFloat] > ShadowTypeSizeBits[kDouble] ||296ShadowTypeSizeBits[kDouble] > ShadowTypeSizeBits[kLongDouble])297report_fatal_error("Invalid nsan mapping: { float->f" +298Twine(ShadowTypeSizeBits[kFloat]) + "; double->f" +299Twine(ShadowTypeSizeBits[kDouble]) +300"; long double->f" +301Twine(ShadowTypeSizeBits[kLongDouble]) + " }");302}303304const ShadowTypeConfig &byValueType(FTValueType VT) const {305assert(VT < FTValueType::kNumValueTypes && "invalid value type");306return *Configs[VT];307}308309// Returns the extended shadow type for a given application type.310Type *getExtendedFPType(Type *FT) const {311if (const auto VT = ftValueTypeFromType(FT))312return Configs[*VT]->getType(Context);313if (FT->isVectorTy()) {314auto *VecTy = cast<VectorType>(FT);315// TODO: add support for scalable vector types.316if (VecTy->isScalableTy())317return nullptr;318Type *ExtendedScalar = getExtendedFPType(VecTy->getElementType());319return ExtendedScalar320? VectorType::get(ExtendedScalar, VecTy->getElementCount())321: nullptr;322}323return nullptr;324}325326private:327LLVMContext &Context;328std::unique_ptr<ShadowTypeConfig> Configs[FTValueType::kNumValueTypes];329};330331// The memory extents of a type specifies how many elements of a given332// FTValueType needs to be stored when storing this type.333struct MemoryExtents {334FTValueType ValueType;335uint64_t NumElts;336};337338static MemoryExtents getMemoryExtentsOrDie(Type *FT) {339if (const auto VT = ftValueTypeFromType(FT))340return {*VT, 1};341if (auto *VecTy = dyn_cast<VectorType>(FT)) {342const auto ScalarExtents = getMemoryExtentsOrDie(VecTy->getElementType());343return {ScalarExtents.ValueType,344ScalarExtents.NumElts * VecTy->getElementCount().getFixedValue()};345}346llvm_unreachable("invalid value type");347}348349// The location of a check. Passed as parameters to runtime checking functions.350class CheckLoc {351public:352// Creates a location that references an application memory location.353static CheckLoc makeStore(Value *Address) {354CheckLoc Result(kStore);355Result.Address = Address;356return Result;357}358static CheckLoc makeLoad(Value *Address) {359CheckLoc Result(kLoad);360Result.Address = Address;361return Result;362}363364// Creates a location that references an argument, given by id.365static CheckLoc makeArg(int ArgId) {366CheckLoc Result(kArg);367Result.ArgId = ArgId;368return Result;369}370371// Creates a location that references the return value of a function.372static CheckLoc makeRet() { return CheckLoc(kRet); }373374// Creates a location that references a vector insert.375static CheckLoc makeInsert() { return CheckLoc(kInsert); }376377// Returns the CheckType of location this refers to, as an integer-typed LLVM378// IR value.379Value *getType(LLVMContext &C) const {380return ConstantInt::get(Type::getInt32Ty(C), static_cast<int>(CheckTy));381}382383// Returns a CheckType-specific value representing details of the location384// (e.g. application address for loads or stores), as an `IntptrTy`-typed LLVM385// IR value.386Value *getValue(Type *IntptrTy, IRBuilder<> &Builder) const {387switch (CheckTy) {388case kUnknown:389llvm_unreachable("unknown type");390case kRet:391case kInsert:392return ConstantInt::get(IntptrTy, 0);393case kArg:394return ConstantInt::get(IntptrTy, ArgId);395case kLoad:396case kStore:397return Builder.CreatePtrToInt(Address, IntptrTy);398}399llvm_unreachable("Unhandled CheckType enum");400}401402private:403// Must be kept in sync with the runtime,404// see compiler-rt/lib/nsan/nsan_stats.h405enum CheckType {406kUnknown = 0,407kRet,408kArg,409kLoad,410kStore,411kInsert,412};413explicit CheckLoc(CheckType CheckTy) : CheckTy(CheckTy) {}414415Value *Address = nullptr;416const CheckType CheckTy;417int ArgId = -1;418};419420// A map of LLVM IR values to shadow LLVM IR values.421class ValueToShadowMap {422public:423explicit ValueToShadowMap(const MappingConfig &Config) : Config(Config) {}424425ValueToShadowMap(const ValueToShadowMap &) = delete;426ValueToShadowMap &operator=(const ValueToShadowMap &) = delete;427428// Sets the shadow value for a value. Asserts that the value does not already429// have a value.430void setShadow(Value &V, Value &Shadow) {431[[maybe_unused]] const bool Inserted = Map.try_emplace(&V, &Shadow).second;432LLVM_DEBUG({433if (!Inserted) {434if (auto *I = dyn_cast<Instruction>(&V))435errs() << I->getFunction()->getName() << ": ";436errs() << "duplicate shadow (" << &V << "): ";437V.dump();438}439});440assert(Inserted && "duplicate shadow");441}442443// Returns true if the value already has a shadow (including if the value is a444// constant). If true, calling getShadow() is valid.445bool hasShadow(Value *V) const {446return isa<Constant>(V) || (Map.find(V) != Map.end());447}448449// Returns the shadow value for a given value. Asserts that the value has450// a shadow value. Lazily creates shadows for constant values.451Value *getShadow(Value *V) const {452if (Constant *C = dyn_cast<Constant>(V))453return getShadowConstant(C);454return Map.find(V)->second;455}456457bool empty() const { return Map.empty(); }458459private:460// Extends a constant application value to its shadow counterpart.461APFloat extendConstantFP(APFloat CV, const fltSemantics &To) const {462bool LosesInfo = false;463CV.convert(To, APFloatBase::rmTowardZero, &LosesInfo);464return CV;465}466467// Returns the shadow constant for the given application constant.468Constant *getShadowConstant(Constant *C) const {469if (UndefValue *U = dyn_cast<UndefValue>(C)) {470return UndefValue::get(Config.getExtendedFPType(U->getType()));471}472if (ConstantFP *CFP = dyn_cast<ConstantFP>(C)) {473// Floating-point constants.474Type *Ty = Config.getExtendedFPType(CFP->getType());475return ConstantFP::get(476Ty, extendConstantFP(CFP->getValueAPF(), Ty->getFltSemantics()));477}478// Vector, array, or aggregate constants.479if (C->getType()->isVectorTy()) {480SmallVector<Constant *, 8> Elements;481for (int I = 0, E = cast<VectorType>(C->getType())482->getElementCount()483.getFixedValue();484I < E; ++I)485Elements.push_back(getShadowConstant(C->getAggregateElement(I)));486return ConstantVector::get(Elements);487}488llvm_unreachable("unimplemented");489}490491const MappingConfig &Config;492DenseMap<Value *, Value *> Map;493};494495/// Instantiating NumericalStabilitySanitizer inserts the nsan runtime library496/// API function declarations into the module if they don't exist already.497/// Instantiating ensures the __nsan_init function is in the list of global498/// constructors for the module.499class NumericalStabilitySanitizer {500public:501NumericalStabilitySanitizer(Module &M);502bool sanitizeFunction(Function &F, const TargetLibraryInfo &TLI);503504private:505bool instrumentMemIntrinsic(MemIntrinsic *MI);506void maybeAddSuffixForNsanInterface(CallBase *CI);507bool addrPointsToConstantData(Value *Addr);508void maybeCreateShadowValue(Instruction &Root, const TargetLibraryInfo &TLI,509ValueToShadowMap &Map);510Value *createShadowValueWithOperandsAvailable(Instruction &Inst,511const TargetLibraryInfo &TLI,512const ValueToShadowMap &Map);513PHINode *maybeCreateShadowPhi(PHINode &Phi, const TargetLibraryInfo &TLI);514void createShadowArguments(Function &F, const TargetLibraryInfo &TLI,515ValueToShadowMap &Map);516517void populateShadowStack(CallBase &CI, const TargetLibraryInfo &TLI,518const ValueToShadowMap &Map);519520void propagateShadowValues(Instruction &Inst, const TargetLibraryInfo &TLI,521const ValueToShadowMap &Map);522Value *emitCheck(Value *V, Value *ShadowV, IRBuilder<> &Builder,523CheckLoc Loc);524Value *emitCheckInternal(Value *V, Value *ShadowV, IRBuilder<> &Builder,525CheckLoc Loc);526void emitFCmpCheck(FCmpInst &FCmp, const ValueToShadowMap &Map);527528// Value creation handlers.529Value *handleLoad(LoadInst &Load, Type *VT, Type *ExtendedVT);530Value *handleCallBase(CallBase &Call, Type *VT, Type *ExtendedVT,531const TargetLibraryInfo &TLI,532const ValueToShadowMap &Map, IRBuilder<> &Builder);533Value *maybeHandleKnownCallBase(CallBase &Call, Type *VT, Type *ExtendedVT,534const TargetLibraryInfo &TLI,535const ValueToShadowMap &Map,536IRBuilder<> &Builder);537Value *handleTrunc(const FPTruncInst &Trunc, Type *VT, Type *ExtendedVT,538const ValueToShadowMap &Map, IRBuilder<> &Builder);539Value *handleExt(const FPExtInst &Ext, Type *VT, Type *ExtendedVT,540const ValueToShadowMap &Map, IRBuilder<> &Builder);541542// Value propagation handlers.543void propagateFTStore(StoreInst &Store, Type *VT, Type *ExtendedVT,544const ValueToShadowMap &Map);545void propagateNonFTStore(StoreInst &Store, Type *VT,546const ValueToShadowMap &Map);547548const DataLayout &DL;549LLVMContext &Context;550MappingConfig Config;551IntegerType *IntptrTy = nullptr;552FunctionCallee NsanGetShadowPtrForStore[FTValueType::kNumValueTypes] = {};553FunctionCallee NsanGetShadowPtrForLoad[FTValueType::kNumValueTypes] = {};554FunctionCallee NsanCheckValue[FTValueType::kNumValueTypes] = {};555FunctionCallee NsanFCmpFail[FTValueType::kNumValueTypes] = {};556FunctionCallee NsanCopyValues;557FunctionCallee NsanSetValueUnknown;558FunctionCallee NsanGetRawShadowTypePtr;559FunctionCallee NsanGetRawShadowPtr;560GlobalValue *NsanShadowRetTag = nullptr;561562Type *NsanShadowRetType = nullptr;563GlobalValue *NsanShadowRetPtr = nullptr;564565GlobalValue *NsanShadowArgsTag = nullptr;566567Type *NsanShadowArgsType = nullptr;568GlobalValue *NsanShadowArgsPtr = nullptr;569570std::optional<Regex> CheckFunctionsFilter;571};572} // end anonymous namespace573574PreservedAnalyses575NumericalStabilitySanitizerPass::run(Module &M, ModuleAnalysisManager &MAM) {576getOrCreateSanitizerCtorAndInitFunctions(577M, kNsanModuleCtorName, kNsanInitName, /*InitArgTypes=*/{},578/*InitArgs=*/{},579// This callback is invoked when the functions are created the first580// time. Hook them into the global ctors list in that case:581[&](Function *Ctor, FunctionCallee) { appendToGlobalCtors(M, Ctor, 0); });582583NumericalStabilitySanitizer Nsan(M);584auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();585for (Function &F : M)586Nsan.sanitizeFunction(F, FAM.getResult<TargetLibraryAnalysis>(F));587588return PreservedAnalyses::none();589}590591static GlobalValue *createThreadLocalGV(const char *Name, Module &M, Type *Ty) {592return dyn_cast<GlobalValue>(M.getOrInsertGlobal(Name, Ty, [&M, Ty, Name] {593return new GlobalVariable(M, Ty, false, GlobalVariable::ExternalLinkage,594nullptr, Name, nullptr,595GlobalVariable::InitialExecTLSModel);596}));597}598599NumericalStabilitySanitizer::NumericalStabilitySanitizer(Module &M)600: DL(M.getDataLayout()), Context(M.getContext()), Config(Context) {601IntptrTy = DL.getIntPtrType(Context);602Type *PtrTy = PointerType::getUnqual(Context);603Type *Int32Ty = Type::getInt32Ty(Context);604Type *Int1Ty = Type::getInt1Ty(Context);605Type *VoidTy = Type::getVoidTy(Context);606607AttributeList Attr;608Attr = Attr.addFnAttribute(Context, Attribute::NoUnwind);609// Initialize the runtime values (functions and global variables).610for (int I = 0; I < kNumValueTypes; ++I) {611const FTValueType VT = static_cast<FTValueType>(I);612const char *VTName = typeNameFromFTValueType(VT);613Type *VTTy = typeFromFTValueType(VT, Context);614615// Load/store.616const std::string GetterPrefix =617std::string("__nsan_get_shadow_ptr_for_") + VTName;618NsanGetShadowPtrForStore[VT] = M.getOrInsertFunction(619GetterPrefix + "_store", Attr, PtrTy, PtrTy, IntptrTy);620NsanGetShadowPtrForLoad[VT] = M.getOrInsertFunction(621GetterPrefix + "_load", Attr, PtrTy, PtrTy, IntptrTy);622623// Check.624const auto &ShadowConfig = Config.byValueType(VT);625Type *ShadowTy = ShadowConfig.getType(Context);626NsanCheckValue[VT] =627M.getOrInsertFunction(std::string("__nsan_internal_check_") + VTName +628"_" + ShadowConfig.getNsanTypeId(),629Attr, Int32Ty, VTTy, ShadowTy, Int32Ty, IntptrTy);630NsanFCmpFail[VT] = M.getOrInsertFunction(631std::string("__nsan_fcmp_fail_") + VTName + "_" +632ShadowConfig.getNsanTypeId(),633Attr, VoidTy, VTTy, VTTy, ShadowTy, ShadowTy, Int32Ty, Int1Ty, Int1Ty);634}635636NsanCopyValues = M.getOrInsertFunction("__nsan_copy_values", Attr, VoidTy,637PtrTy, PtrTy, IntptrTy);638NsanSetValueUnknown = M.getOrInsertFunction("__nsan_set_value_unknown", Attr,639VoidTy, PtrTy, IntptrTy);640641// TODO: Add attributes nofree, nosync, readnone, readonly,642NsanGetRawShadowTypePtr = M.getOrInsertFunction(643"__nsan_internal_get_raw_shadow_type_ptr", Attr, PtrTy, PtrTy);644NsanGetRawShadowPtr = M.getOrInsertFunction(645"__nsan_internal_get_raw_shadow_ptr", Attr, PtrTy, PtrTy);646647NsanShadowRetTag = createThreadLocalGV("__nsan_shadow_ret_tag", M, IntptrTy);648649NsanShadowRetType = ArrayType::get(Type::getInt8Ty(Context),650kMaxVectorWidth * kMaxShadowTypeSizeBytes);651NsanShadowRetPtr =652createThreadLocalGV("__nsan_shadow_ret_ptr", M, NsanShadowRetType);653654NsanShadowArgsTag =655createThreadLocalGV("__nsan_shadow_args_tag", M, IntptrTy);656657NsanShadowArgsType =658ArrayType::get(Type::getInt8Ty(Context),659kMaxVectorWidth * kMaxNumArgs * kMaxShadowTypeSizeBytes);660661NsanShadowArgsPtr =662createThreadLocalGV("__nsan_shadow_args_ptr", M, NsanShadowArgsType);663664if (!ClCheckFunctionsFilter.empty()) {665Regex R = Regex(ClCheckFunctionsFilter);666std::string RegexError;667assert(R.isValid(RegexError));668CheckFunctionsFilter = std::move(R);669}670}671672// Returns true if the given LLVM Value points to constant data (typically, a673// global variable reference).674bool NumericalStabilitySanitizer::addrPointsToConstantData(Value *Addr) {675// If this is a GEP, just analyze its pointer operand.676if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Addr))677Addr = GEP->getPointerOperand();678679if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Addr))680return GV->isConstant();681return false;682}683684// This instruments the function entry to create shadow arguments.685// Pseudocode:686// if (this_fn_ptr == __nsan_shadow_args_tag) {687// s(arg0) = LOAD<sizeof(arg0)>(__nsan_shadow_args);688// s(arg1) = LOAD<sizeof(arg1)>(__nsan_shadow_args + sizeof(arg0));689// ...690// __nsan_shadow_args_tag = 0;691// } else {692// s(arg0) = fext(arg0);693// s(arg1) = fext(arg1);694// ...695// }696void NumericalStabilitySanitizer::createShadowArguments(697Function &F, const TargetLibraryInfo &TLI, ValueToShadowMap &Map) {698assert(!F.getIntrinsicID() && "found a definition of an intrinsic");699700// Do not bother if there are no FP args.701if (all_of(F.args(), [this](const Argument &Arg) {702return Config.getExtendedFPType(Arg.getType()) == nullptr;703}))704return;705706IRBuilder<> Builder(F.getEntryBlock().getFirstNonPHI());707// The function has shadow args if the shadow args tag matches the function708// address.709Value *HasShadowArgs = Builder.CreateICmpEQ(710Builder.CreateLoad(IntptrTy, NsanShadowArgsTag, /*isVolatile=*/false),711Builder.CreatePtrToInt(&F, IntptrTy));712713unsigned ShadowArgsOffsetBytes = 0;714for (Argument &Arg : F.args()) {715Type *VT = Arg.getType();716Type *ExtendedVT = Config.getExtendedFPType(VT);717if (ExtendedVT == nullptr)718continue; // Not an FT value.719Value *L = Builder.CreateAlignedLoad(720ExtendedVT,721Builder.CreateConstGEP2_64(NsanShadowArgsType, NsanShadowArgsPtr, 0,722ShadowArgsOffsetBytes),723Align(1), /*isVolatile=*/false);724Value *Shadow = Builder.CreateSelect(HasShadowArgs, L,725Builder.CreateFPExt(&Arg, ExtendedVT));726Map.setShadow(Arg, *Shadow);727TypeSize SlotSize = DL.getTypeStoreSize(ExtendedVT);728assert(!SlotSize.isScalable() && "unsupported");729ShadowArgsOffsetBytes += SlotSize;730}731Builder.CreateStore(ConstantInt::get(IntptrTy, 0), NsanShadowArgsTag);732}733734// Returns true if the instrumentation should emit code to check arguments735// before a function call.736static bool shouldCheckArgs(CallBase &CI, const TargetLibraryInfo &TLI,737const std::optional<Regex> &CheckFunctionsFilter) {738739Function *Fn = CI.getCalledFunction();740741if (CheckFunctionsFilter) {742// Skip checking args of indirect calls.743if (Fn == nullptr)744return false;745if (CheckFunctionsFilter->match(Fn->getName()))746return true;747return false;748}749750if (Fn == nullptr)751return true; // Always check args of indirect calls.752753// Never check nsan functions, the user called them for a reason.754if (Fn->getName().starts_with("__nsan_"))755return false;756757const auto ID = Fn->getIntrinsicID();758LibFunc LFunc = LibFunc::NumLibFuncs;759// Always check args of unknown functions.760if (ID == Intrinsic::ID() && !TLI.getLibFunc(*Fn, LFunc))761return true;762763// Do not check args of an `fabs` call that is used for a comparison.764// This is typically used for `fabs(a-b) < tolerance`, where what matters is765// the result of the comparison, which is already caught be the fcmp checks.766if (ID == Intrinsic::fabs || LFunc == LibFunc_fabsf ||767LFunc == LibFunc_fabs || LFunc == LibFunc_fabsl)768for (const auto &U : CI.users())769if (isa<CmpInst>(U))770return false;771772return true; // Default is check.773}774775// Populates the shadow call stack (which contains shadow values for every776// floating-point parameter to the function).777void NumericalStabilitySanitizer::populateShadowStack(778CallBase &CI, const TargetLibraryInfo &TLI, const ValueToShadowMap &Map) {779// Do not create a shadow stack for inline asm.780if (CI.isInlineAsm())781return;782783// Do not bother if there are no FP args.784if (all_of(CI.operands(), [this](const Value *Arg) {785return Config.getExtendedFPType(Arg->getType()) == nullptr;786}))787return;788789IRBuilder<> Builder(&CI);790SmallVector<Value *, 8> ArgShadows;791const bool ShouldCheckArgs = shouldCheckArgs(CI, TLI, CheckFunctionsFilter);792for (auto [ArgIdx, Arg] : enumerate(CI.operands())) {793if (Config.getExtendedFPType(Arg->getType()) == nullptr)794continue; // Not an FT value.795Value *ArgShadow = Map.getShadow(Arg);796ArgShadows.push_back(ShouldCheckArgs ? emitCheck(Arg, ArgShadow, Builder,797CheckLoc::makeArg(ArgIdx))798: ArgShadow);799}800801// Do not create shadow stacks for intrinsics/known lib funcs.802if (Function *Fn = CI.getCalledFunction()) {803LibFunc LFunc;804if (Fn->isIntrinsic() || TLI.getLibFunc(*Fn, LFunc))805return;806}807808// Set the shadow stack tag.809Builder.CreateStore(CI.getCalledOperand(), NsanShadowArgsTag);810TypeSize ShadowArgsOffsetBytes = TypeSize::getFixed(0);811812unsigned ShadowArgId = 0;813for (const Value *Arg : CI.operands()) {814Type *VT = Arg->getType();815Type *ExtendedVT = Config.getExtendedFPType(VT);816if (ExtendedVT == nullptr)817continue; // Not an FT value.818Builder.CreateAlignedStore(819ArgShadows[ShadowArgId++],820Builder.CreateConstGEP2_64(NsanShadowArgsType, NsanShadowArgsPtr, 0,821ShadowArgsOffsetBytes),822Align(1), /*isVolatile=*/false);823TypeSize SlotSize = DL.getTypeStoreSize(ExtendedVT);824assert(!SlotSize.isScalable() && "unsupported");825ShadowArgsOffsetBytes += SlotSize;826}827}828829// Internal part of emitCheck(). Returns a value that indicates whether830// computation should continue with the shadow or resume by re-fextending the831// value.832enum class ContinuationType { // Keep in sync with runtime.833ContinueWithShadow = 0,834ResumeFromValue = 1,835};836837Value *NumericalStabilitySanitizer::emitCheckInternal(Value *V, Value *ShadowV,838IRBuilder<> &Builder,839CheckLoc Loc) {840// Do not emit checks for constant values, this is redundant.841if (isa<Constant>(V))842return ConstantInt::get(843Builder.getInt32Ty(),844static_cast<int>(ContinuationType::ContinueWithShadow));845846Type *Ty = V->getType();847if (const auto VT = ftValueTypeFromType(Ty))848return Builder.CreateCall(849NsanCheckValue[*VT],850{V, ShadowV, Loc.getType(Context), Loc.getValue(IntptrTy, Builder)});851852if (Ty->isVectorTy()) {853auto *VecTy = cast<VectorType>(Ty);854// We currently skip scalable vector types in MappingConfig,855// thus we should not encounter any such types here.856assert(!VecTy->isScalableTy() &&857"Scalable vector types are not supported yet");858Value *CheckResult = nullptr;859for (int I = 0, E = VecTy->getElementCount().getFixedValue(); I < E; ++I) {860// We resume if any element resumes. Another option would be to create a861// vector shuffle with the array of ContinueWithShadow, but that is too862// complex.863Value *ExtractV = Builder.CreateExtractElement(V, I);864Value *ExtractShadowV = Builder.CreateExtractElement(ShadowV, I);865Value *ComponentCheckResult =866emitCheckInternal(ExtractV, ExtractShadowV, Builder, Loc);867CheckResult = CheckResult868? Builder.CreateOr(CheckResult, ComponentCheckResult)869: ComponentCheckResult;870}871return CheckResult;872}873if (Ty->isArrayTy()) {874Value *CheckResult = nullptr;875for (auto I : seq(Ty->getArrayNumElements())) {876Value *ExtractV = Builder.CreateExtractElement(V, I);877Value *ExtractShadowV = Builder.CreateExtractElement(ShadowV, I);878Value *ComponentCheckResult =879emitCheckInternal(ExtractV, ExtractShadowV, Builder, Loc);880CheckResult = CheckResult881? Builder.CreateOr(CheckResult, ComponentCheckResult)882: ComponentCheckResult;883}884return CheckResult;885}886if (Ty->isStructTy()) {887Value *CheckResult = nullptr;888for (auto I : seq(Ty->getStructNumElements())) {889if (Config.getExtendedFPType(Ty->getStructElementType(I)) == nullptr)890continue; // Only check FT values.891Value *ExtractV = Builder.CreateExtractValue(V, I);892Value *ExtractShadowV = Builder.CreateExtractElement(ShadowV, I);893Value *ComponentCheckResult =894emitCheckInternal(ExtractV, ExtractShadowV, Builder, Loc);895CheckResult = CheckResult896? Builder.CreateOr(CheckResult, ComponentCheckResult)897: ComponentCheckResult;898}899if (!CheckResult)900return ConstantInt::get(901Builder.getInt32Ty(),902static_cast<int>(ContinuationType::ContinueWithShadow));903return CheckResult;904}905906llvm_unreachable("not implemented");907}908909// Inserts a runtime check of V against its shadow value ShadowV.910// We check values whenever they escape: on return, call, stores, and911// insertvalue.912// Returns the shadow value that should be used to continue the computations,913// depending on the answer from the runtime.914// TODO: Should we check on select ? phi ?915Value *NumericalStabilitySanitizer::emitCheck(Value *V, Value *ShadowV,916IRBuilder<> &Builder,917CheckLoc Loc) {918// Do not emit checks for constant values, this is redundant.919if (isa<Constant>(V))920return ShadowV;921922if (Instruction *Inst = dyn_cast<Instruction>(V)) {923Function *F = Inst->getFunction();924if (CheckFunctionsFilter && !CheckFunctionsFilter->match(F->getName())) {925return ShadowV;926}927}928929Value *CheckResult = emitCheckInternal(V, ShadowV, Builder, Loc);930Value *ICmpEQ = Builder.CreateICmpEQ(931CheckResult,932ConstantInt::get(Builder.getInt32Ty(),933static_cast<int>(ContinuationType::ResumeFromValue)));934return Builder.CreateSelect(935ICmpEQ, Builder.CreateFPExt(V, Config.getExtendedFPType(V->getType())),936ShadowV);937}938939// Inserts a check that fcmp on shadow values are consistent with that on base940// values.941void NumericalStabilitySanitizer::emitFCmpCheck(FCmpInst &FCmp,942const ValueToShadowMap &Map) {943if (!ClInstrumentFCmp)944return;945946Function *F = FCmp.getFunction();947if (CheckFunctionsFilter && !CheckFunctionsFilter->match(F->getName()))948return;949950Value *LHS = FCmp.getOperand(0);951if (Config.getExtendedFPType(LHS->getType()) == nullptr)952return;953Value *RHS = FCmp.getOperand(1);954955// Split the basic block. On mismatch, we'll jump to the new basic block with956// a call to the runtime for error reporting.957BasicBlock *FCmpBB = FCmp.getParent();958BasicBlock *NextBB = FCmpBB->splitBasicBlock(FCmp.getNextNode());959// Remove the newly created terminator unconditional branch.960FCmpBB->back().eraseFromParent();961BasicBlock *FailBB =962BasicBlock::Create(Context, "", FCmpBB->getParent(), NextBB);963964// Create the shadow fcmp and comparison between the fcmps.965IRBuilder<> FCmpBuilder(FCmpBB);966FCmpBuilder.SetCurrentDebugLocation(FCmp.getDebugLoc());967Value *ShadowLHS = Map.getShadow(LHS);968Value *ShadowRHS = Map.getShadow(RHS);969// See comment on ClTruncateFCmpEq.970if (FCmp.isEquality() && ClTruncateFCmpEq) {971Type *Ty = ShadowLHS->getType();972ShadowLHS = FCmpBuilder.CreateFPExt(973FCmpBuilder.CreateFPTrunc(ShadowLHS, LHS->getType()), Ty);974ShadowRHS = FCmpBuilder.CreateFPExt(975FCmpBuilder.CreateFPTrunc(ShadowRHS, RHS->getType()), Ty);976}977Value *ShadowFCmp =978FCmpBuilder.CreateFCmp(FCmp.getPredicate(), ShadowLHS, ShadowRHS);979Value *OriginalAndShadowFcmpMatch =980FCmpBuilder.CreateICmpEQ(&FCmp, ShadowFCmp);981982if (OriginalAndShadowFcmpMatch->getType()->isVectorTy()) {983// If we have a vector type, `OriginalAndShadowFcmpMatch` is a vector of i1,984// where an element is true if the corresponding elements in original and985// shadow are the same. We want all elements to be 1.986OriginalAndShadowFcmpMatch =987FCmpBuilder.CreateAndReduce(OriginalAndShadowFcmpMatch);988}989990// Use MDBuilder(*C).createLikelyBranchWeights() because "match" is the common991// case.992FCmpBuilder.CreateCondBr(OriginalAndShadowFcmpMatch, NextBB, FailBB,993MDBuilder(Context).createLikelyBranchWeights());994995// Fill in FailBB.996IRBuilder<> FailBuilder(FailBB);997FailBuilder.SetCurrentDebugLocation(FCmp.getDebugLoc());998999const auto EmitFailCall = [this, &FCmp, &FCmpBuilder,1000&FailBuilder](Value *L, Value *R, Value *ShadowL,1001Value *ShadowR, Value *Result,1002Value *ShadowResult) {1003Type *FT = L->getType();1004FunctionCallee *Callee = nullptr;1005if (FT->isFloatTy()) {1006Callee = &(NsanFCmpFail[kFloat]);1007} else if (FT->isDoubleTy()) {1008Callee = &(NsanFCmpFail[kDouble]);1009} else if (FT->isX86_FP80Ty()) {1010// TODO: make NsanFCmpFailLongDouble work.1011Callee = &(NsanFCmpFail[kDouble]);1012L = FailBuilder.CreateFPTrunc(L, Type::getDoubleTy(Context));1013R = FailBuilder.CreateFPTrunc(L, Type::getDoubleTy(Context));1014} else {1015llvm_unreachable("not implemented");1016}1017FailBuilder.CreateCall(*Callee, {L, R, ShadowL, ShadowR,1018ConstantInt::get(FCmpBuilder.getInt32Ty(),1019FCmp.getPredicate()),1020Result, ShadowResult});1021};1022if (LHS->getType()->isVectorTy()) {1023for (int I = 0, E = cast<VectorType>(LHS->getType())1024->getElementCount()1025.getFixedValue();1026I < E; ++I) {1027Value *ExtractLHS = FailBuilder.CreateExtractElement(LHS, I);1028Value *ExtractRHS = FailBuilder.CreateExtractElement(RHS, I);1029Value *ExtractShaodwLHS = FailBuilder.CreateExtractElement(ShadowLHS, I);1030Value *ExtractShaodwRHS = FailBuilder.CreateExtractElement(ShadowRHS, I);1031Value *ExtractFCmp = FailBuilder.CreateExtractElement(&FCmp, I);1032Value *ExtractShadowFCmp =1033FailBuilder.CreateExtractElement(ShadowFCmp, I);1034EmitFailCall(ExtractLHS, ExtractRHS, ExtractShaodwLHS, ExtractShaodwRHS,1035ExtractFCmp, ExtractShadowFCmp);1036}1037} else {1038EmitFailCall(LHS, RHS, ShadowLHS, ShadowRHS, &FCmp, ShadowFCmp);1039}1040FailBuilder.CreateBr(NextBB);10411042++NumInstrumentedFCmp;1043}10441045// Creates a shadow phi value for any phi that defines a value of FT type.1046PHINode *NumericalStabilitySanitizer::maybeCreateShadowPhi(1047PHINode &Phi, const TargetLibraryInfo &TLI) {1048Type *VT = Phi.getType();1049Type *ExtendedVT = Config.getExtendedFPType(VT);1050if (ExtendedVT == nullptr)1051return nullptr; // Not an FT value.1052// The phi operands are shadow values and are not available when the phi is1053// created. They will be populated in a final phase, once all shadow values1054// have been created.1055PHINode *Shadow = PHINode::Create(ExtendedVT, Phi.getNumIncomingValues());1056Shadow->insertAfter(&Phi);1057return Shadow;1058}10591060Value *NumericalStabilitySanitizer::handleLoad(LoadInst &Load, Type *VT,1061Type *ExtendedVT) {1062IRBuilder<> Builder(Load.getNextNode());1063Builder.SetCurrentDebugLocation(Load.getDebugLoc());1064if (addrPointsToConstantData(Load.getPointerOperand())) {1065// No need to look into the shadow memory, the value is a constant. Just1066// convert from FT to 2FT.1067return Builder.CreateFPExt(&Load, ExtendedVT);1068}10691070// if (%shadowptr == &)1071// %shadow = fpext %v1072// else1073// %shadow = load (ptrcast %shadow_ptr))1074// Considered options here:1075// - Have `NsanGetShadowPtrForLoad` return a fixed address1076// &__nsan_unknown_value_shadow_address that is valid to load from, and1077// use a select. This has the advantage that the generated IR is simpler.1078// - Have `NsanGetShadowPtrForLoad` return nullptr. Because `select` does1079// not short-circuit, dereferencing the returned pointer is no longer an1080// option, have to split and create a separate basic block. This has the1081// advantage of being easier to debug because it crashes if we ever mess1082// up.10831084const auto Extents = getMemoryExtentsOrDie(VT);1085Value *ShadowPtr = Builder.CreateCall(1086NsanGetShadowPtrForLoad[Extents.ValueType],1087{Load.getPointerOperand(), ConstantInt::get(IntptrTy, Extents.NumElts)});1088++NumInstrumentedFTLoads;10891090// Split the basic block.1091BasicBlock *LoadBB = Load.getParent();1092BasicBlock *NextBB = LoadBB->splitBasicBlock(Builder.GetInsertPoint());1093// Create the two options for creating the shadow value.1094BasicBlock *ShadowLoadBB =1095BasicBlock::Create(Context, "", LoadBB->getParent(), NextBB);1096BasicBlock *FExtBB =1097BasicBlock::Create(Context, "", LoadBB->getParent(), NextBB);10981099// Replace the newly created terminator unconditional branch by a conditional1100// branch to one of the options.1101{1102LoadBB->back().eraseFromParent();1103IRBuilder<> LoadBBBuilder(LoadBB); // The old builder has been invalidated.1104LoadBBBuilder.SetCurrentDebugLocation(Load.getDebugLoc());1105LoadBBBuilder.CreateCondBr(LoadBBBuilder.CreateIsNull(ShadowPtr), FExtBB,1106ShadowLoadBB);1107}11081109// Fill in ShadowLoadBB.1110IRBuilder<> ShadowLoadBBBuilder(ShadowLoadBB);1111ShadowLoadBBBuilder.SetCurrentDebugLocation(Load.getDebugLoc());1112Value *ShadowLoad = ShadowLoadBBBuilder.CreateAlignedLoad(1113ExtendedVT, ShadowPtr, Align(1), Load.isVolatile());1114if (ClCheckLoads) {1115ShadowLoad = emitCheck(&Load, ShadowLoad, ShadowLoadBBBuilder,1116CheckLoc::makeLoad(Load.getPointerOperand()));1117}1118ShadowLoadBBBuilder.CreateBr(NextBB);11191120// Fill in FExtBB.1121IRBuilder<> FExtBBBuilder(FExtBB);1122FExtBBBuilder.SetCurrentDebugLocation(Load.getDebugLoc());1123Value *FExt = FExtBBBuilder.CreateFPExt(&Load, ExtendedVT);1124FExtBBBuilder.CreateBr(NextBB);11251126// The shadow value come from any of the options.1127IRBuilder<> NextBBBuilder(&*NextBB->begin());1128NextBBBuilder.SetCurrentDebugLocation(Load.getDebugLoc());1129PHINode *ShadowPhi = NextBBBuilder.CreatePHI(ExtendedVT, 2);1130ShadowPhi->addIncoming(ShadowLoad, ShadowLoadBB);1131ShadowPhi->addIncoming(FExt, FExtBB);1132return ShadowPhi;1133}11341135Value *NumericalStabilitySanitizer::handleTrunc(const FPTruncInst &Trunc,1136Type *VT, Type *ExtendedVT,1137const ValueToShadowMap &Map,1138IRBuilder<> &Builder) {1139Value *OrigSource = Trunc.getOperand(0);1140Type *OrigSourceTy = OrigSource->getType();1141Type *ExtendedSourceTy = Config.getExtendedFPType(OrigSourceTy);11421143// When truncating:1144// - (A) If the source has a shadow, we truncate from the shadow, else we1145// truncate from the original source.1146// - (B) If the shadow of the source is larger than the shadow of the dest,1147// we still need a truncate. Else, the shadow of the source is the same1148// type as the shadow of the dest (because mappings are non-decreasing), so1149// we don't need to emit a truncate.1150// Examples,1151// with a mapping of {f32->f64;f64->f80;f80->f128}1152// fptrunc double %1 to float -> fptrunc x86_fp80 s(%1) to double1153// fptrunc x86_fp80 %1 to float -> fptrunc fp128 s(%1) to double1154// fptrunc fp128 %1 to float -> fptrunc fp128 %1 to double1155// fptrunc x86_fp80 %1 to double -> x86_fp80 s(%1)1156// fptrunc fp128 %1 to double -> fptrunc fp128 %1 to x86_fp801157// fptrunc fp128 %1 to x86_fp80 -> fp128 %11158// with a mapping of {f32->f64;f64->f128;f80->f128}1159// fptrunc double %1 to float -> fptrunc fp128 s(%1) to double1160// fptrunc x86_fp80 %1 to float -> fptrunc fp128 s(%1) to double1161// fptrunc fp128 %1 to float -> fptrunc fp128 %1 to double1162// fptrunc x86_fp80 %1 to double -> fp128 %11163// fptrunc fp128 %1 to double -> fp128 %11164// fptrunc fp128 %1 to x86_fp80 -> fp128 %11165// with a mapping of {f32->f32;f64->f32;f80->f64}1166// fptrunc double %1 to float -> float s(%1)1167// fptrunc x86_fp80 %1 to float -> fptrunc double s(%1) to float1168// fptrunc fp128 %1 to float -> fptrunc fp128 %1 to float1169// fptrunc x86_fp80 %1 to double -> fptrunc double s(%1) to float1170// fptrunc fp128 %1 to double -> fptrunc fp128 %1 to float1171// fptrunc fp128 %1 to x86_fp80 -> fptrunc fp128 %1 to double11721173// See (A) above.1174Value *Source = ExtendedSourceTy ? Map.getShadow(OrigSource) : OrigSource;1175Type *SourceTy = ExtendedSourceTy ? ExtendedSourceTy : OrigSourceTy;1176// See (B) above.1177if (SourceTy == ExtendedVT)1178return Source;11791180return Builder.CreateFPTrunc(Source, ExtendedVT);1181}11821183Value *NumericalStabilitySanitizer::handleExt(const FPExtInst &Ext, Type *VT,1184Type *ExtendedVT,1185const ValueToShadowMap &Map,1186IRBuilder<> &Builder) {1187Value *OrigSource = Ext.getOperand(0);1188Type *OrigSourceTy = OrigSource->getType();1189Type *ExtendedSourceTy = Config.getExtendedFPType(OrigSourceTy);1190// When extending:1191// - (A) If the source has a shadow, we extend from the shadow, else we1192// extend from the original source.1193// - (B) If the shadow of the dest is larger than the shadow of the source,1194// we still need an extend. Else, the shadow of the source is the same1195// type as the shadow of the dest (because mappings are non-decreasing), so1196// we don't need to emit an extend.1197// Examples,1198// with a mapping of {f32->f64;f64->f80;f80->f128}1199// fpext half %1 to float -> fpext half %1 to double1200// fpext half %1 to double -> fpext half %1 to x86_fp801201// fpext half %1 to x86_fp80 -> fpext half %1 to fp1281202// fpext float %1 to double -> double s(%1)1203// fpext float %1 to x86_fp80 -> fpext double s(%1) to fp1281204// fpext double %1 to x86_fp80 -> fpext x86_fp80 s(%1) to fp1281205// with a mapping of {f32->f64;f64->f128;f80->f128}1206// fpext half %1 to float -> fpext half %1 to double1207// fpext half %1 to double -> fpext half %1 to fp1281208// fpext half %1 to x86_fp80 -> fpext half %1 to fp1281209// fpext float %1 to double -> fpext double s(%1) to fp1281210// fpext float %1 to x86_fp80 -> fpext double s(%1) to fp1281211// fpext double %1 to x86_fp80 -> fp128 s(%1)1212// with a mapping of {f32->f32;f64->f32;f80->f64}1213// fpext half %1 to float -> fpext half %1 to float1214// fpext half %1 to double -> fpext half %1 to float1215// fpext half %1 to x86_fp80 -> fpext half %1 to double1216// fpext float %1 to double -> s(%1)1217// fpext float %1 to x86_fp80 -> fpext float s(%1) to double1218// fpext double %1 to x86_fp80 -> fpext float s(%1) to double12191220// See (A) above.1221Value *Source = ExtendedSourceTy ? Map.getShadow(OrigSource) : OrigSource;1222Type *SourceTy = ExtendedSourceTy ? ExtendedSourceTy : OrigSourceTy;1223// See (B) above.1224if (SourceTy == ExtendedVT)1225return Source;12261227return Builder.CreateFPExt(Source, ExtendedVT);1228}12291230namespace {1231// TODO: This should be tablegen-ed.1232struct KnownIntrinsic {1233struct WidenedIntrinsic {1234const char *NarrowName;1235Intrinsic::ID ID; // wide id.1236using FnTypeFactory = FunctionType *(*)(LLVMContext &);1237FnTypeFactory MakeFnTy;1238};12391240static const char *get(LibFunc LFunc);12411242// Given an intrinsic with an `FT` argument, try to find a wider intrinsic1243// that applies the same operation on the shadow argument.1244// Options are:1245// - pass in the ID and full function type,1246// - pass in the name, which includes the function type through mangling.1247static const WidenedIntrinsic *widen(StringRef Name);12481249private:1250struct LFEntry {1251LibFunc LFunc;1252const char *IntrinsicName;1253};1254static const LFEntry kLibfuncIntrinsics[];12551256static const WidenedIntrinsic kWidenedIntrinsics[];1257};1258} // namespace12591260static FunctionType *makeDoubleDouble(LLVMContext &C) {1261return FunctionType::get(Type::getDoubleTy(C), {Type::getDoubleTy(C)}, false);1262}12631264static FunctionType *makeX86FP80X86FP80(LLVMContext &C) {1265return FunctionType::get(Type::getX86_FP80Ty(C), {Type::getX86_FP80Ty(C)},1266false);1267}12681269static FunctionType *makeDoubleDoubleI32(LLVMContext &C) {1270return FunctionType::get(Type::getDoubleTy(C),1271{Type::getDoubleTy(C), Type::getInt32Ty(C)}, false);1272}12731274static FunctionType *makeX86FP80X86FP80I32(LLVMContext &C) {1275return FunctionType::get(Type::getX86_FP80Ty(C),1276{Type::getX86_FP80Ty(C), Type::getInt32Ty(C)},1277false);1278}12791280static FunctionType *makeDoubleDoubleDouble(LLVMContext &C) {1281return FunctionType::get(Type::getDoubleTy(C),1282{Type::getDoubleTy(C), Type::getDoubleTy(C)}, false);1283}12841285static FunctionType *makeX86FP80X86FP80X86FP80(LLVMContext &C) {1286return FunctionType::get(Type::getX86_FP80Ty(C),1287{Type::getX86_FP80Ty(C), Type::getX86_FP80Ty(C)},1288false);1289}12901291static FunctionType *makeDoubleDoubleDoubleDouble(LLVMContext &C) {1292return FunctionType::get(1293Type::getDoubleTy(C),1294{Type::getDoubleTy(C), Type::getDoubleTy(C), Type::getDoubleTy(C)},1295false);1296}12971298static FunctionType *makeX86FP80X86FP80X86FP80X86FP80(LLVMContext &C) {1299return FunctionType::get(1300Type::getX86_FP80Ty(C),1301{Type::getX86_FP80Ty(C), Type::getX86_FP80Ty(C), Type::getX86_FP80Ty(C)},1302false);1303}13041305const KnownIntrinsic::WidenedIntrinsic KnownIntrinsic::kWidenedIntrinsics[] = {1306// TODO: Right now we ignore vector intrinsics.1307// This is hard because we have to model the semantics of the intrinsics,1308// e.g. llvm.x86.sse2.min.sd means extract first element, min, insert back.1309// Intrinsics that take any non-vector FT types:1310// NOTE: Right now because of1311// https://github.com/llvm/llvm-project/issues/447441312// for f128 we need to use makeX86FP80X86FP80 (go to a lower precision and1313// come back).1314{"llvm.sqrt.f32", Intrinsic::sqrt, makeDoubleDouble},1315{"llvm.sqrt.f64", Intrinsic::sqrt, makeX86FP80X86FP80},1316{"llvm.sqrt.f80", Intrinsic::sqrt, makeX86FP80X86FP80},1317{"llvm.powi.f32", Intrinsic::powi, makeDoubleDoubleI32},1318{"llvm.powi.f64", Intrinsic::powi, makeX86FP80X86FP80I32},1319{"llvm.powi.f80", Intrinsic::powi, makeX86FP80X86FP80I32},1320{"llvm.sin.f32", Intrinsic::sin, makeDoubleDouble},1321{"llvm.sin.f64", Intrinsic::sin, makeX86FP80X86FP80},1322{"llvm.sin.f80", Intrinsic::sin, makeX86FP80X86FP80},1323{"llvm.cos.f32", Intrinsic::cos, makeDoubleDouble},1324{"llvm.cos.f64", Intrinsic::cos, makeX86FP80X86FP80},1325{"llvm.cos.f80", Intrinsic::cos, makeX86FP80X86FP80},1326{"llvm.pow.f32", Intrinsic::pow, makeDoubleDoubleDouble},1327{"llvm.pow.f64", Intrinsic::pow, makeX86FP80X86FP80X86FP80},1328{"llvm.pow.f80", Intrinsic::pow, makeX86FP80X86FP80X86FP80},1329{"llvm.exp.f32", Intrinsic::exp, makeDoubleDouble},1330{"llvm.exp.f64", Intrinsic::exp, makeX86FP80X86FP80},1331{"llvm.exp.f80", Intrinsic::exp, makeX86FP80X86FP80},1332{"llvm.exp2.f32", Intrinsic::exp2, makeDoubleDouble},1333{"llvm.exp2.f64", Intrinsic::exp2, makeX86FP80X86FP80},1334{"llvm.exp2.f80", Intrinsic::exp2, makeX86FP80X86FP80},1335{"llvm.log.f32", Intrinsic::log, makeDoubleDouble},1336{"llvm.log.f64", Intrinsic::log, makeX86FP80X86FP80},1337{"llvm.log.f80", Intrinsic::log, makeX86FP80X86FP80},1338{"llvm.log10.f32", Intrinsic::log10, makeDoubleDouble},1339{"llvm.log10.f64", Intrinsic::log10, makeX86FP80X86FP80},1340{"llvm.log10.f80", Intrinsic::log10, makeX86FP80X86FP80},1341{"llvm.log2.f32", Intrinsic::log2, makeDoubleDouble},1342{"llvm.log2.f64", Intrinsic::log2, makeX86FP80X86FP80},1343{"llvm.log2.f80", Intrinsic::log2, makeX86FP80X86FP80},1344{"llvm.fma.f32", Intrinsic::fma, makeDoubleDoubleDoubleDouble},13451346{"llvm.fmuladd.f32", Intrinsic::fmuladd, makeDoubleDoubleDoubleDouble},13471348{"llvm.fma.f64", Intrinsic::fma, makeX86FP80X86FP80X86FP80X86FP80},13491350{"llvm.fmuladd.f64", Intrinsic::fma, makeX86FP80X86FP80X86FP80X86FP80},13511352{"llvm.fma.f80", Intrinsic::fma, makeX86FP80X86FP80X86FP80X86FP80},1353{"llvm.fabs.f32", Intrinsic::fabs, makeDoubleDouble},1354{"llvm.fabs.f64", Intrinsic::fabs, makeX86FP80X86FP80},1355{"llvm.fabs.f80", Intrinsic::fabs, makeX86FP80X86FP80},1356{"llvm.minnum.f32", Intrinsic::minnum, makeDoubleDoubleDouble},1357{"llvm.minnum.f64", Intrinsic::minnum, makeX86FP80X86FP80X86FP80},1358{"llvm.minnum.f80", Intrinsic::minnum, makeX86FP80X86FP80X86FP80},1359{"llvm.maxnum.f32", Intrinsic::maxnum, makeDoubleDoubleDouble},1360{"llvm.maxnum.f64", Intrinsic::maxnum, makeX86FP80X86FP80X86FP80},1361{"llvm.maxnum.f80", Intrinsic::maxnum, makeX86FP80X86FP80X86FP80},1362{"llvm.minimum.f32", Intrinsic::minimum, makeDoubleDoubleDouble},1363{"llvm.minimum.f64", Intrinsic::minimum, makeX86FP80X86FP80X86FP80},1364{"llvm.minimum.f80", Intrinsic::minimum, makeX86FP80X86FP80X86FP80},1365{"llvm.maximum.f32", Intrinsic::maximum, makeDoubleDoubleDouble},1366{"llvm.maximum.f64", Intrinsic::maximum, makeX86FP80X86FP80X86FP80},1367{"llvm.maximum.f80", Intrinsic::maximum, makeX86FP80X86FP80X86FP80},1368{"llvm.copysign.f32", Intrinsic::copysign, makeDoubleDoubleDouble},1369{"llvm.copysign.f64", Intrinsic::copysign, makeX86FP80X86FP80X86FP80},1370{"llvm.copysign.f80", Intrinsic::copysign, makeX86FP80X86FP80X86FP80},1371{"llvm.floor.f32", Intrinsic::floor, makeDoubleDouble},1372{"llvm.floor.f64", Intrinsic::floor, makeX86FP80X86FP80},1373{"llvm.floor.f80", Intrinsic::floor, makeX86FP80X86FP80},1374{"llvm.ceil.f32", Intrinsic::ceil, makeDoubleDouble},1375{"llvm.ceil.f64", Intrinsic::ceil, makeX86FP80X86FP80},1376{"llvm.ceil.f80", Intrinsic::ceil, makeX86FP80X86FP80},1377{"llvm.trunc.f32", Intrinsic::trunc, makeDoubleDouble},1378{"llvm.trunc.f64", Intrinsic::trunc, makeX86FP80X86FP80},1379{"llvm.trunc.f80", Intrinsic::trunc, makeX86FP80X86FP80},1380{"llvm.rint.f32", Intrinsic::rint, makeDoubleDouble},1381{"llvm.rint.f64", Intrinsic::rint, makeX86FP80X86FP80},1382{"llvm.rint.f80", Intrinsic::rint, makeX86FP80X86FP80},1383{"llvm.nearbyint.f32", Intrinsic::nearbyint, makeDoubleDouble},1384{"llvm.nearbyint.f64", Intrinsic::nearbyint, makeX86FP80X86FP80},1385{"llvm.nearbyin80f64", Intrinsic::nearbyint, makeX86FP80X86FP80},1386{"llvm.round.f32", Intrinsic::round, makeDoubleDouble},1387{"llvm.round.f64", Intrinsic::round, makeX86FP80X86FP80},1388{"llvm.round.f80", Intrinsic::round, makeX86FP80X86FP80},1389{"llvm.lround.f32", Intrinsic::lround, makeDoubleDouble},1390{"llvm.lround.f64", Intrinsic::lround, makeX86FP80X86FP80},1391{"llvm.lround.f80", Intrinsic::lround, makeX86FP80X86FP80},1392{"llvm.llround.f32", Intrinsic::llround, makeDoubleDouble},1393{"llvm.llround.f64", Intrinsic::llround, makeX86FP80X86FP80},1394{"llvm.llround.f80", Intrinsic::llround, makeX86FP80X86FP80},1395{"llvm.lrint.f32", Intrinsic::lrint, makeDoubleDouble},1396{"llvm.lrint.f64", Intrinsic::lrint, makeX86FP80X86FP80},1397{"llvm.lrint.f80", Intrinsic::lrint, makeX86FP80X86FP80},1398{"llvm.llrint.f32", Intrinsic::llrint, makeDoubleDouble},1399{"llvm.llrint.f64", Intrinsic::llrint, makeX86FP80X86FP80},1400{"llvm.llrint.f80", Intrinsic::llrint, makeX86FP80X86FP80},1401};14021403const KnownIntrinsic::LFEntry KnownIntrinsic::kLibfuncIntrinsics[] = {1404{LibFunc_sqrtf, "llvm.sqrt.f32"},1405{LibFunc_sqrt, "llvm.sqrt.f64"},1406{LibFunc_sqrtl, "llvm.sqrt.f80"},1407{LibFunc_sinf, "llvm.sin.f32"},1408{LibFunc_sin, "llvm.sin.f64"},1409{LibFunc_sinl, "llvm.sin.f80"},1410{LibFunc_cosf, "llvm.cos.f32"},1411{LibFunc_cos, "llvm.cos.f64"},1412{LibFunc_cosl, "llvm.cos.f80"},1413{LibFunc_powf, "llvm.pow.f32"},1414{LibFunc_pow, "llvm.pow.f64"},1415{LibFunc_powl, "llvm.pow.f80"},1416{LibFunc_expf, "llvm.exp.f32"},1417{LibFunc_exp, "llvm.exp.f64"},1418{LibFunc_expl, "llvm.exp.f80"},1419{LibFunc_exp2f, "llvm.exp2.f32"},1420{LibFunc_exp2, "llvm.exp2.f64"},1421{LibFunc_exp2l, "llvm.exp2.f80"},1422{LibFunc_logf, "llvm.log.f32"},1423{LibFunc_log, "llvm.log.f64"},1424{LibFunc_logl, "llvm.log.f80"},1425{LibFunc_log10f, "llvm.log10.f32"},1426{LibFunc_log10, "llvm.log10.f64"},1427{LibFunc_log10l, "llvm.log10.f80"},1428{LibFunc_log2f, "llvm.log2.f32"},1429{LibFunc_log2, "llvm.log2.f64"},1430{LibFunc_log2l, "llvm.log2.f80"},1431{LibFunc_fabsf, "llvm.fabs.f32"},1432{LibFunc_fabs, "llvm.fabs.f64"},1433{LibFunc_fabsl, "llvm.fabs.f80"},1434{LibFunc_copysignf, "llvm.copysign.f32"},1435{LibFunc_copysign, "llvm.copysign.f64"},1436{LibFunc_copysignl, "llvm.copysign.f80"},1437{LibFunc_floorf, "llvm.floor.f32"},1438{LibFunc_floor, "llvm.floor.f64"},1439{LibFunc_floorl, "llvm.floor.f80"},1440{LibFunc_fmaxf, "llvm.maxnum.f32"},1441{LibFunc_fmax, "llvm.maxnum.f64"},1442{LibFunc_fmaxl, "llvm.maxnum.f80"},1443{LibFunc_fminf, "llvm.minnum.f32"},1444{LibFunc_fmin, "llvm.minnum.f64"},1445{LibFunc_fminl, "llvm.minnum.f80"},1446{LibFunc_ceilf, "llvm.ceil.f32"},1447{LibFunc_ceil, "llvm.ceil.f64"},1448{LibFunc_ceill, "llvm.ceil.f80"},1449{LibFunc_truncf, "llvm.trunc.f32"},1450{LibFunc_trunc, "llvm.trunc.f64"},1451{LibFunc_truncl, "llvm.trunc.f80"},1452{LibFunc_rintf, "llvm.rint.f32"},1453{LibFunc_rint, "llvm.rint.f64"},1454{LibFunc_rintl, "llvm.rint.f80"},1455{LibFunc_nearbyintf, "llvm.nearbyint.f32"},1456{LibFunc_nearbyint, "llvm.nearbyint.f64"},1457{LibFunc_nearbyintl, "llvm.nearbyint.f80"},1458{LibFunc_roundf, "llvm.round.f32"},1459{LibFunc_round, "llvm.round.f64"},1460{LibFunc_roundl, "llvm.round.f80"},1461};14621463const char *KnownIntrinsic::get(LibFunc LFunc) {1464for (const auto &E : kLibfuncIntrinsics) {1465if (E.LFunc == LFunc)1466return E.IntrinsicName;1467}1468return nullptr;1469}14701471const KnownIntrinsic::WidenedIntrinsic *KnownIntrinsic::widen(StringRef Name) {1472for (const auto &E : kWidenedIntrinsics) {1473if (E.NarrowName == Name)1474return &E;1475}1476return nullptr;1477}14781479// Returns the name of the LLVM intrinsic corresponding to the given function.1480static const char *getIntrinsicFromLibfunc(Function &Fn, Type *VT,1481const TargetLibraryInfo &TLI) {1482LibFunc LFunc;1483if (!TLI.getLibFunc(Fn, LFunc))1484return nullptr;14851486if (const char *Name = KnownIntrinsic::get(LFunc))1487return Name;14881489LLVM_DEBUG(errs() << "TODO: LibFunc: " << TLI.getName(LFunc) << "\n");1490return nullptr;1491}14921493// Try to handle a known function call.1494Value *NumericalStabilitySanitizer::maybeHandleKnownCallBase(1495CallBase &Call, Type *VT, Type *ExtendedVT, const TargetLibraryInfo &TLI,1496const ValueToShadowMap &Map, IRBuilder<> &Builder) {1497Function *Fn = Call.getCalledFunction();1498if (Fn == nullptr)1499return nullptr;15001501Intrinsic::ID WidenedId = Intrinsic::ID();1502FunctionType *WidenedFnTy = nullptr;1503if (const auto ID = Fn->getIntrinsicID()) {1504const auto *Widened = KnownIntrinsic::widen(Fn->getName());1505if (Widened) {1506WidenedId = Widened->ID;1507WidenedFnTy = Widened->MakeFnTy(Context);1508} else {1509// If we don't know how to widen the intrinsic, we have no choice but to1510// call the non-wide version on a truncated shadow and extend again1511// afterwards.1512WidenedId = ID;1513WidenedFnTy = Fn->getFunctionType();1514}1515} else if (const char *Name = getIntrinsicFromLibfunc(*Fn, VT, TLI)) {1516// We might have a call to a library function that we can replace with a1517// wider Intrinsic.1518const auto *Widened = KnownIntrinsic::widen(Name);1519assert(Widened && "make sure KnownIntrinsic entries are consistent");1520WidenedId = Widened->ID;1521WidenedFnTy = Widened->MakeFnTy(Context);1522} else {1523// This is not a known library function or intrinsic.1524return nullptr;1525}15261527// Check that the widened intrinsic is valid.1528SmallVector<Intrinsic::IITDescriptor, 8> Table;1529getIntrinsicInfoTableEntries(WidenedId, Table);1530SmallVector<Type *, 4> ArgTys;1531ArrayRef<Intrinsic::IITDescriptor> TableRef = Table;1532[[maybe_unused]] Intrinsic::MatchIntrinsicTypesResult MatchResult =1533Intrinsic::matchIntrinsicSignature(WidenedFnTy, TableRef, ArgTys);1534assert(MatchResult == Intrinsic::MatchIntrinsicTypes_Match &&1535"invalid widened intrinsic");1536// For known intrinsic functions, we create a second call to the same1537// intrinsic with a different type.1538SmallVector<Value *, 4> Args;1539// The last operand is the intrinsic itself, skip it.1540for (unsigned I = 0, E = Call.getNumOperands() - 1; I < E; ++I) {1541Value *Arg = Call.getOperand(I);1542Type *OrigArgTy = Arg->getType();1543Type *IntrinsicArgTy = WidenedFnTy->getParamType(I);1544if (OrigArgTy == IntrinsicArgTy) {1545Args.push_back(Arg); // The arg is passed as is.1546continue;1547}1548Type *ShadowArgTy = Config.getExtendedFPType(Arg->getType());1549assert(ShadowArgTy &&1550"don't know how to get the shadow value for a non-FT");1551Value *Shadow = Map.getShadow(Arg);1552if (ShadowArgTy == IntrinsicArgTy) {1553// The shadow is the right type for the intrinsic.1554assert(Shadow->getType() == ShadowArgTy);1555Args.push_back(Shadow);1556continue;1557}1558// There is no intrinsic with his level of precision, truncate the shadow.1559Args.push_back(Builder.CreateFPTrunc(Shadow, IntrinsicArgTy));1560}1561Value *IntrinsicCall = Builder.CreateIntrinsic(WidenedId, ArgTys, Args);1562return WidenedFnTy->getReturnType() == ExtendedVT1563? IntrinsicCall1564: Builder.CreateFPExt(IntrinsicCall, ExtendedVT);1565}15661567// Handle a CallBase, i.e. a function call, an inline asm sequence, or an1568// invoke.1569Value *NumericalStabilitySanitizer::handleCallBase(CallBase &Call, Type *VT,1570Type *ExtendedVT,1571const TargetLibraryInfo &TLI,1572const ValueToShadowMap &Map,1573IRBuilder<> &Builder) {1574// We cannot look inside inline asm, just expand the result again.1575if (Call.isInlineAsm())1576return Builder.CreateFPExt(&Call, ExtendedVT);15771578// Intrinsics and library functions (e.g. sin, exp) are handled1579// specifically, because we know their semantics and can do better than1580// blindly calling them (e.g. compute the sinus in the actual shadow domain).1581if (Value *V =1582maybeHandleKnownCallBase(Call, VT, ExtendedVT, TLI, Map, Builder))1583return V;15841585// If the return tag matches that of the called function, read the extended1586// return value from the shadow ret ptr. Else, just extend the return value.1587Value *L =1588Builder.CreateLoad(IntptrTy, NsanShadowRetTag, /*isVolatile=*/false);1589Value *HasShadowRet = Builder.CreateICmpEQ(1590L, Builder.CreatePtrToInt(Call.getCalledOperand(), IntptrTy));15911592Value *ShadowRetVal = Builder.CreateLoad(1593ExtendedVT,1594Builder.CreateConstGEP2_64(NsanShadowRetType, NsanShadowRetPtr, 0, 0),1595/*isVolatile=*/false);1596Value *Shadow = Builder.CreateSelect(HasShadowRet, ShadowRetVal,1597Builder.CreateFPExt(&Call, ExtendedVT));1598++NumInstrumentedFTCalls;1599return Shadow;1600}16011602// Creates a shadow value for the given FT value. At that point all operands are1603// guaranteed to be available.1604Value *NumericalStabilitySanitizer::createShadowValueWithOperandsAvailable(1605Instruction &Inst, const TargetLibraryInfo &TLI,1606const ValueToShadowMap &Map) {1607Type *VT = Inst.getType();1608Type *ExtendedVT = Config.getExtendedFPType(VT);1609assert(ExtendedVT != nullptr && "trying to create a shadow for a non-FT");16101611if (auto *Load = dyn_cast<LoadInst>(&Inst))1612return handleLoad(*Load, VT, ExtendedVT);16131614if (auto *Call = dyn_cast<CallInst>(&Inst)) {1615// Insert after the call.1616BasicBlock::iterator It(Inst);1617IRBuilder<> Builder(Call->getParent(), ++It);1618Builder.SetCurrentDebugLocation(Call->getDebugLoc());1619return handleCallBase(*Call, VT, ExtendedVT, TLI, Map, Builder);1620}16211622if (auto *Invoke = dyn_cast<InvokeInst>(&Inst)) {1623// The Invoke terminates the basic block, create a new basic block in1624// between the successful invoke and the next block.1625BasicBlock *InvokeBB = Invoke->getParent();1626BasicBlock *NextBB = Invoke->getNormalDest();1627BasicBlock *NewBB =1628BasicBlock::Create(Context, "", NextBB->getParent(), NextBB);1629Inst.replaceSuccessorWith(NextBB, NewBB);16301631IRBuilder<> Builder(NewBB);1632Builder.SetCurrentDebugLocation(Invoke->getDebugLoc());1633Value *Shadow = handleCallBase(*Invoke, VT, ExtendedVT, TLI, Map, Builder);1634Builder.CreateBr(NextBB);1635NewBB->replaceSuccessorsPhiUsesWith(InvokeBB, NewBB);1636return Shadow;1637}16381639IRBuilder<> Builder(Inst.getNextNode());1640Builder.SetCurrentDebugLocation(Inst.getDebugLoc());16411642if (auto *Trunc = dyn_cast<FPTruncInst>(&Inst))1643return handleTrunc(*Trunc, VT, ExtendedVT, Map, Builder);1644if (auto *Ext = dyn_cast<FPExtInst>(&Inst))1645return handleExt(*Ext, VT, ExtendedVT, Map, Builder);16461647if (auto *UnaryOp = dyn_cast<UnaryOperator>(&Inst))1648return Builder.CreateUnOp(UnaryOp->getOpcode(),1649Map.getShadow(UnaryOp->getOperand(0)));16501651if (auto *BinOp = dyn_cast<BinaryOperator>(&Inst))1652return Builder.CreateBinOp(BinOp->getOpcode(),1653Map.getShadow(BinOp->getOperand(0)),1654Map.getShadow(BinOp->getOperand(1)));16551656if (isa<UIToFPInst>(&Inst) || isa<SIToFPInst>(&Inst)) {1657auto *Cast = dyn_cast<CastInst>(&Inst);1658return Builder.CreateCast(Cast->getOpcode(), Cast->getOperand(0),1659ExtendedVT);1660}16611662if (auto *S = dyn_cast<SelectInst>(&Inst))1663return Builder.CreateSelect(S->getCondition(),1664Map.getShadow(S->getTrueValue()),1665Map.getShadow(S->getFalseValue()));16661667if (auto *Extract = dyn_cast<ExtractElementInst>(&Inst))1668return Builder.CreateExtractElement(1669Map.getShadow(Extract->getVectorOperand()), Extract->getIndexOperand());16701671if (auto *Insert = dyn_cast<InsertElementInst>(&Inst))1672return Builder.CreateInsertElement(Map.getShadow(Insert->getOperand(0)),1673Map.getShadow(Insert->getOperand(1)),1674Insert->getOperand(2));16751676if (auto *Shuffle = dyn_cast<ShuffleVectorInst>(&Inst))1677return Builder.CreateShuffleVector(Map.getShadow(Shuffle->getOperand(0)),1678Map.getShadow(Shuffle->getOperand(1)),1679Shuffle->getShuffleMask());1680// TODO: We could make aggregate object first class citizens. For now we1681// just extend the extracted value.1682if (auto *Extract = dyn_cast<ExtractValueInst>(&Inst))1683return Builder.CreateFPExt(Extract, ExtendedVT);16841685if (auto *BC = dyn_cast<BitCastInst>(&Inst))1686return Builder.CreateFPExt(BC, ExtendedVT);16871688report_fatal_error("Unimplemented support for " +1689Twine(Inst.getOpcodeName()));1690}16911692// Creates a shadow value for an instruction that defines a value of FT type.1693// FT operands that do not already have shadow values are created recursively.1694// The DFS is guaranteed to not loop as phis and arguments already have1695// shadows.1696void NumericalStabilitySanitizer::maybeCreateShadowValue(1697Instruction &Root, const TargetLibraryInfo &TLI, ValueToShadowMap &Map) {1698Type *VT = Root.getType();1699Type *ExtendedVT = Config.getExtendedFPType(VT);1700if (ExtendedVT == nullptr)1701return; // Not an FT value.17021703if (Map.hasShadow(&Root))1704return; // Shadow already exists.17051706assert(!isa<PHINode>(Root) && "phi nodes should already have shadows");17071708std::vector<Instruction *> DfsStack(1, &Root);1709while (!DfsStack.empty()) {1710// Ensure that all operands to the instruction have shadows before1711// proceeding.1712Instruction *I = DfsStack.back();1713// The shadow for the instruction might have been created deeper in the DFS,1714// see `forward_use_with_two_uses` test.1715if (Map.hasShadow(I)) {1716DfsStack.pop_back();1717continue;1718}17191720bool MissingShadow = false;1721for (Value *Op : I->operands()) {1722Type *VT = Op->getType();1723if (!Config.getExtendedFPType(VT))1724continue; // Not an FT value.1725if (Map.hasShadow(Op))1726continue; // Shadow is already available.1727MissingShadow = true;1728DfsStack.push_back(cast<Instruction>(Op));1729}1730if (MissingShadow)1731continue; // Process operands and come back to this instruction later.17321733// All operands have shadows. Create a shadow for the current value.1734Value *Shadow = createShadowValueWithOperandsAvailable(*I, TLI, Map);1735Map.setShadow(*I, *Shadow);1736DfsStack.pop_back();1737}1738}17391740// A floating-point store needs its value and type written to shadow memory.1741void NumericalStabilitySanitizer::propagateFTStore(1742StoreInst &Store, Type *VT, Type *ExtendedVT, const ValueToShadowMap &Map) {1743Value *StoredValue = Store.getValueOperand();1744IRBuilder<> Builder(&Store);1745Builder.SetCurrentDebugLocation(Store.getDebugLoc());1746const auto Extents = getMemoryExtentsOrDie(VT);1747Value *ShadowPtr = Builder.CreateCall(1748NsanGetShadowPtrForStore[Extents.ValueType],1749{Store.getPointerOperand(), ConstantInt::get(IntptrTy, Extents.NumElts)});17501751Value *StoredShadow = Map.getShadow(StoredValue);1752if (!Store.getParent()->getParent()->hasOptNone()) {1753// Only check stores when optimizing, because non-optimized code generates1754// too many stores to the stack, creating false positives.1755if (ClCheckStores) {1756StoredShadow = emitCheck(StoredValue, StoredShadow, Builder,1757CheckLoc::makeStore(Store.getPointerOperand()));1758++NumInstrumentedFTStores;1759}1760}17611762Builder.CreateAlignedStore(StoredShadow, ShadowPtr, Align(1),1763Store.isVolatile());1764}17651766// A non-ft store needs to invalidate shadow memory. Exceptions are:1767// - memory transfers of floating-point data through other pointer types (llvm1768// optimization passes transform `*(float*)a = *(float*)b` into1769// `*(i32*)a = *(i32*)b` ). These have the same semantics as memcpy.1770// - Writes of FT-sized constants. LLVM likes to do float stores as bitcasted1771// ints. Note that this is not really necessary because if the value is1772// unknown the framework will re-extend it on load anyway. It just felt1773// easier to debug tests with vectors of FTs.1774void NumericalStabilitySanitizer::propagateNonFTStore(1775StoreInst &Store, Type *VT, const ValueToShadowMap &Map) {1776Value *PtrOp = Store.getPointerOperand();1777IRBuilder<> Builder(Store.getNextNode());1778Builder.SetCurrentDebugLocation(Store.getDebugLoc());1779Value *Dst = PtrOp;1780TypeSize SlotSize = DL.getTypeStoreSize(VT);1781assert(!SlotSize.isScalable() && "unsupported");1782const auto LoadSizeBytes = SlotSize.getFixedValue();1783Value *ValueSize = Constant::getIntegerValue(1784IntptrTy, APInt(IntptrTy->getPrimitiveSizeInBits(), LoadSizeBytes));17851786++NumInstrumentedNonFTStores;1787Value *StoredValue = Store.getValueOperand();1788if (LoadInst *Load = dyn_cast<LoadInst>(StoredValue)) {1789// TODO: Handle the case when the value is from a phi.1790// This is a memory transfer with memcpy semantics. Copy the type and1791// value from the source. Note that we cannot use __nsan_copy_values()1792// here, because that will not work when there is a write to memory in1793// between the load and the store, e.g. in the case of a swap.1794Type *ShadowTypeIntTy = Type::getIntNTy(Context, 8 * LoadSizeBytes);1795Type *ShadowValueIntTy =1796Type::getIntNTy(Context, 8 * kShadowScale * LoadSizeBytes);1797IRBuilder<> LoadBuilder(Load->getNextNode());1798Builder.SetCurrentDebugLocation(Store.getDebugLoc());1799Value *LoadSrc = Load->getPointerOperand();1800// Read the shadow type and value at load time. The type has the same size1801// as the FT value, the value has twice its size.1802// TODO: cache them to avoid re-creating them when a load is used by1803// several stores. Maybe create them like the FT shadows when a load is1804// encountered.1805Value *RawShadowType = LoadBuilder.CreateAlignedLoad(1806ShadowTypeIntTy,1807LoadBuilder.CreateCall(NsanGetRawShadowTypePtr, {LoadSrc}), Align(1),1808/*isVolatile=*/false);1809Value *RawShadowValue = LoadBuilder.CreateAlignedLoad(1810ShadowValueIntTy,1811LoadBuilder.CreateCall(NsanGetRawShadowPtr, {LoadSrc}), Align(1),1812/*isVolatile=*/false);18131814// Write back the shadow type and value at store time.1815Builder.CreateAlignedStore(1816RawShadowType, Builder.CreateCall(NsanGetRawShadowTypePtr, {Dst}),1817Align(1),1818/*isVolatile=*/false);1819Builder.CreateAlignedStore(RawShadowValue,1820Builder.CreateCall(NsanGetRawShadowPtr, {Dst}),1821Align(1),1822/*isVolatile=*/false);18231824++NumInstrumentedNonFTMemcpyStores;1825return;1826}1827// ClPropagateNonFTConstStoresAsFT is by default false.1828if (Constant *C; ClPropagateNonFTConstStoresAsFT &&1829(C = dyn_cast<Constant>(StoredValue))) {1830// This might be a fp constant stored as an int. Bitcast and store if it has1831// appropriate size.1832Type *BitcastTy = nullptr; // The FT type to bitcast to.1833if (auto *CInt = dyn_cast<ConstantInt>(C)) {1834switch (CInt->getType()->getScalarSizeInBits()) {1835case 32:1836BitcastTy = Type::getFloatTy(Context);1837break;1838case 64:1839BitcastTy = Type::getDoubleTy(Context);1840break;1841case 80:1842BitcastTy = Type::getX86_FP80Ty(Context);1843break;1844default:1845break;1846}1847} else if (auto *CDV = dyn_cast<ConstantDataVector>(C)) {1848const int NumElements =1849cast<VectorType>(CDV->getType())->getElementCount().getFixedValue();1850switch (CDV->getType()->getScalarSizeInBits()) {1851case 32:1852BitcastTy =1853VectorType::get(Type::getFloatTy(Context), NumElements, false);1854break;1855case 64:1856BitcastTy =1857VectorType::get(Type::getDoubleTy(Context), NumElements, false);1858break;1859case 80:1860BitcastTy =1861VectorType::get(Type::getX86_FP80Ty(Context), NumElements, false);1862break;1863default:1864break;1865}1866}1867if (BitcastTy) {1868const MemoryExtents Extents = getMemoryExtentsOrDie(BitcastTy);1869Value *ShadowPtr = Builder.CreateCall(1870NsanGetShadowPtrForStore[Extents.ValueType],1871{PtrOp, ConstantInt::get(IntptrTy, Extents.NumElts)});1872// Bitcast the integer value to the appropriate FT type and extend to 2FT.1873Type *ExtVT = Config.getExtendedFPType(BitcastTy);1874Value *Shadow =1875Builder.CreateFPExt(Builder.CreateBitCast(C, BitcastTy), ExtVT);1876Builder.CreateAlignedStore(Shadow, ShadowPtr, Align(1),1877Store.isVolatile());1878return;1879}1880}1881// All other stores just reset the shadow value to unknown.1882Builder.CreateCall(NsanSetValueUnknown, {Dst, ValueSize});1883}18841885void NumericalStabilitySanitizer::propagateShadowValues(1886Instruction &Inst, const TargetLibraryInfo &TLI,1887const ValueToShadowMap &Map) {1888if (auto *Store = dyn_cast<StoreInst>(&Inst)) {1889Value *StoredValue = Store->getValueOperand();1890Type *VT = StoredValue->getType();1891Type *ExtendedVT = Config.getExtendedFPType(VT);1892if (ExtendedVT == nullptr)1893return propagateNonFTStore(*Store, VT, Map);1894return propagateFTStore(*Store, VT, ExtendedVT, Map);1895}18961897if (auto *FCmp = dyn_cast<FCmpInst>(&Inst)) {1898emitFCmpCheck(*FCmp, Map);1899return;1900}19011902if (auto *CB = dyn_cast<CallBase>(&Inst)) {1903maybeAddSuffixForNsanInterface(CB);1904if (CallInst *CI = dyn_cast<CallInst>(&Inst))1905maybeMarkSanitizerLibraryCallNoBuiltin(CI, &TLI);1906if (MemIntrinsic *MI = dyn_cast<MemIntrinsic>(&Inst)) {1907instrumentMemIntrinsic(MI);1908return;1909}1910populateShadowStack(*CB, TLI, Map);1911return;1912}19131914if (auto *RetInst = dyn_cast<ReturnInst>(&Inst)) {1915if (!ClCheckRet)1916return;19171918Value *RV = RetInst->getReturnValue();1919if (RV == nullptr)1920return; // This is a `ret void`.1921Type *VT = RV->getType();1922Type *ExtendedVT = Config.getExtendedFPType(VT);1923if (ExtendedVT == nullptr)1924return; // Not an FT ret.1925Value *RVShadow = Map.getShadow(RV);1926IRBuilder<> Builder(RetInst);19271928RVShadow = emitCheck(RV, RVShadow, Builder, CheckLoc::makeRet());1929++NumInstrumentedFTRets;1930// Store tag.1931Value *FnAddr =1932Builder.CreatePtrToInt(Inst.getParent()->getParent(), IntptrTy);1933Builder.CreateStore(FnAddr, NsanShadowRetTag);1934// Store value.1935Value *ShadowRetValPtr =1936Builder.CreateConstGEP2_64(NsanShadowRetType, NsanShadowRetPtr, 0, 0);1937Builder.CreateStore(RVShadow, ShadowRetValPtr);1938return;1939}19401941if (InsertValueInst *Insert = dyn_cast<InsertValueInst>(&Inst)) {1942Value *V = Insert->getOperand(1);1943Type *VT = V->getType();1944Type *ExtendedVT = Config.getExtendedFPType(VT);1945if (ExtendedVT == nullptr)1946return;1947IRBuilder<> Builder(Insert);1948emitCheck(V, Map.getShadow(V), Builder, CheckLoc::makeInsert());1949return;1950}1951}19521953// Moves fast math flags from the function to individual instructions, and1954// removes the attribute from the function.1955// TODO: Make this controllable with a flag.1956static void moveFastMathFlags(Function &F,1957std::vector<Instruction *> &Instructions) {1958FastMathFlags FMF;1959#define MOVE_FLAG(attr, setter) \1960if (F.getFnAttribute(attr).getValueAsString() == "true") { \1961F.removeFnAttr(attr); \1962FMF.set##setter(); \1963}1964MOVE_FLAG("unsafe-fp-math", Fast)1965MOVE_FLAG("no-infs-fp-math", NoInfs)1966MOVE_FLAG("no-nans-fp-math", NoNaNs)1967MOVE_FLAG("no-signed-zeros-fp-math", NoSignedZeros)1968#undef MOVE_FLAG19691970for (Instruction *I : Instructions)1971if (isa<FPMathOperator>(I))1972I->setFastMathFlags(FMF);1973}19741975bool NumericalStabilitySanitizer::sanitizeFunction(1976Function &F, const TargetLibraryInfo &TLI) {1977if (!F.hasFnAttribute(Attribute::SanitizeNumericalStability))1978return false;19791980// This is required to prevent instrumenting call to __nsan_init from within1981// the module constructor.1982if (F.getName() == kNsanModuleCtorName)1983return false;1984SmallVector<Instruction *, 8> AllLoadsAndStores;1985SmallVector<Instruction *, 8> LocalLoadsAndStores;19861987// The instrumentation maintains:1988// - for each IR value `v` of floating-point (or vector floating-point) type1989// FT, a shadow IR value `s(v)` with twice the precision 2FT (e.g.1990// double for float and f128 for double).1991// - A shadow memory, which stores `s(v)` for any `v` that has been stored,1992// along with a shadow memory tag, which stores whether the value in the1993// corresponding shadow memory is valid. Note that this might be1994// incorrect if a non-instrumented function stores to memory, or if1995// memory is stored to through a char pointer.1996// - A shadow stack, which holds `s(v)` for any floating-point argument `v`1997// of a call to an instrumented function. This allows1998// instrumented functions to retrieve the shadow values for their1999// arguments.2000// Because instrumented functions can be called from non-instrumented2001// functions, the stack needs to include a tag so that the instrumented2002// function knows whether shadow values are available for their2003// parameters (i.e. whether is was called by an instrumented function).2004// When shadow arguments are not available, they have to be recreated by2005// extending the precision of the non-shadow arguments to the non-shadow2006// value. Non-instrumented functions do not modify (or even know about) the2007// shadow stack. The shadow stack pointer is __nsan_shadow_args. The shadow2008// stack tag is __nsan_shadow_args_tag. The tag is any unique identifier2009// for the function (we use the address of the function). Both variables2010// are thread local.2011// Example:2012// calls shadow stack tag shadow stack2013// =======================================================================2014// non_instrumented_1() 0 02015// |2016// v2017// instrumented_2(float a) 0 02018// |2019// v2020// instrumented_3(float b, double c) &instrumented_3 s(b),s(c)2021// |2022// v2023// instrumented_4(float d) &instrumented_4 s(d)2024// |2025// v2026// non_instrumented_5(float e) &non_instrumented_5 s(e)2027// |2028// v2029// instrumented_6(float f) &non_instrumented_5 s(e)2030//2031// On entry, instrumented_2 checks whether the tag corresponds to its2032// function ptr.2033// Note that functions reset the tag to 0 after reading shadow parameters.2034// This ensures that the function does not erroneously read invalid data if2035// called twice in the same stack, once from an instrumented function and2036// once from an uninstrumented one. For example, in the following example,2037// resetting the tag in (A) ensures that (B) does not reuse the same the2038// shadow arguments (which would be incorrect).2039// instrumented_1(float a)2040// |2041// v2042// instrumented_2(float b) (A)2043// |2044// v2045// non_instrumented_3()2046// |2047// v2048// instrumented_2(float b) (B)2049//2050// - A shadow return slot. Any function that returns a floating-point value2051// places a shadow return value in __nsan_shadow_ret_val. Again, because2052// we might be calling non-instrumented functions, this value is guarded2053// by __nsan_shadow_ret_tag marker indicating which instrumented function2054// placed the value in __nsan_shadow_ret_val, so that the caller can check2055// that this corresponds to the callee. Both variables are thread local.2056//2057// For example, in the following example, the instrumentation in2058// `instrumented_1` rejects the shadow return value from `instrumented_3`2059// because is is not tagged as expected (`&instrumented_3` instead of2060// `non_instrumented_2`):2061//2062// instrumented_1()2063// |2064// v2065// float non_instrumented_2()2066// |2067// v2068// float instrumented_3()2069//2070// Calls of known math functions (sin, cos, exp, ...) are duplicated to call2071// their overload on the shadow type.20722073// Collect all instructions before processing, as creating shadow values2074// creates new instructions inside the function.2075std::vector<Instruction *> OriginalInstructions;2076for (BasicBlock &BB : F)2077for (Instruction &Inst : BB)2078OriginalInstructions.emplace_back(&Inst);20792080moveFastMathFlags(F, OriginalInstructions);2081ValueToShadowMap ValueToShadow(Config);20822083// In the first pass, we create shadow values for all FT function arguments2084// and all phis. This ensures that the DFS of the next pass does not have2085// any loops.2086std::vector<PHINode *> OriginalPhis;2087createShadowArguments(F, TLI, ValueToShadow);2088for (Instruction *I : OriginalInstructions) {2089if (PHINode *Phi = dyn_cast<PHINode>(I)) {2090if (PHINode *Shadow = maybeCreateShadowPhi(*Phi, TLI)) {2091OriginalPhis.push_back(Phi);2092ValueToShadow.setShadow(*Phi, *Shadow);2093}2094}2095}20962097// Create shadow values for all instructions creating FT values.2098for (Instruction *I : OriginalInstructions)2099maybeCreateShadowValue(*I, TLI, ValueToShadow);21002101// Propagate shadow values across stores, calls and rets.2102for (Instruction *I : OriginalInstructions)2103propagateShadowValues(*I, TLI, ValueToShadow);21042105// The last pass populates shadow phis with shadow values.2106for (PHINode *Phi : OriginalPhis) {2107PHINode *ShadowPhi = dyn_cast<PHINode>(ValueToShadow.getShadow(Phi));2108for (unsigned I : seq(Phi->getNumOperands())) {2109Value *V = Phi->getOperand(I);2110Value *Shadow = ValueToShadow.getShadow(V);2111BasicBlock *IncomingBB = Phi->getIncomingBlock(I);2112// For some instructions (e.g. invoke), we create the shadow in a separate2113// block, different from the block where the original value is created.2114// In that case, the shadow phi might need to refer to this block instead2115// of the original block.2116// Note that this can only happen for instructions as constant shadows are2117// always created in the same block.2118ShadowPhi->addIncoming(Shadow, IncomingBB);2119}2120}21212122return !ValueToShadow.empty();2123}21242125// Instrument the memory intrinsics so that they properly modify the shadow2126// memory.2127bool NumericalStabilitySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {2128IRBuilder<> Builder(MI);2129if (auto *M = dyn_cast<MemSetInst>(MI)) {2130Builder.CreateCall(2131NsanSetValueUnknown,2132{/*Address=*/M->getArgOperand(0),2133/*Size=*/Builder.CreateIntCast(M->getArgOperand(2), IntptrTy, false)});2134} else if (auto *M = dyn_cast<MemTransferInst>(MI)) {2135Builder.CreateCall(2136NsanCopyValues,2137{/*Destination=*/M->getArgOperand(0),2138/*Source=*/M->getArgOperand(1),2139/*Size=*/Builder.CreateIntCast(M->getArgOperand(2), IntptrTy, false)});2140}2141return false;2142}21432144void NumericalStabilitySanitizer::maybeAddSuffixForNsanInterface(CallBase *CI) {2145Function *Fn = CI->getCalledFunction();2146if (Fn == nullptr)2147return;21482149if (!Fn->getName().starts_with("__nsan_"))2150return;21512152if (Fn->getName() == "__nsan_dump_shadow_mem") {2153assert(CI->arg_size() == 4 &&2154"invalid prototype for __nsan_dump_shadow_mem");2155// __nsan_dump_shadow_mem requires an extra parameter with the dynamic2156// configuration:2157// (shadow_type_id_for_long_double << 16) | (shadow_type_id_for_double << 8)2158// | shadow_type_id_for_double2159const uint64_t shadow_value_type_ids =2160(static_cast<size_t>(Config.byValueType(kLongDouble).getNsanTypeId())2161<< 16) |2162(static_cast<size_t>(Config.byValueType(kDouble).getNsanTypeId())2163<< 8) |2164static_cast<size_t>(Config.byValueType(kFloat).getNsanTypeId());2165CI->setArgOperand(3, ConstantInt::get(IntptrTy, shadow_value_type_ids));2166}2167}216821692170