Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -1449,6 +1449,13 @@ BUILTIN(__builtin_ms_va_end, "vc*&", "n") BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n") +// T __builtin_load_no_speculate (const volatile T * ptr, +// const volatile void * lower, +// const volatile void * upper +// [, T failval +// [, const volatile void * cmpptr]]) +BUILTIN(__builtin_load_no_speculate, "vvCD*vCD*vCD*vvCD*", "t") + #undef BUILTIN #undef LIBBUILTIN #undef LANGBUILTIN Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -987,3 +987,5 @@ // A warning group for warnings about code that clang accepts when // compiling OpenCL C/C++ but which is not compatible with the SPIR spec. def SpirCompat : DiagGroup<"spir-compat">; + +def LoadNoSpeculate: DiagGroup<"load-no-speculate">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7108,6 +7108,18 @@ "address argument to nontemporal builtin must be a pointer to integer, float, " "pointer, or a vector of such types (%0 invalid)">; +def err_nospeculateload_builtin_must_be_pointer_to_pointer_or_integral : Error< + "argument to load_no_speculate builtin must be a pointer to a pointer or integer " + "(%0 invalid)">; +def err_nospeculateload_builtin_bounds_cannot_both_be_null : Error< + "lower and upper bounds arguments to load_no_speculate builtin must not both " + "be null">; +def warn_nospeculateload_builtin_target_not_supported : Warning< + "this target does not support anti-speculation operations. Your program will " + "still execute correctly, but speculation will not be inhibited">, + InGroup; + + def err_deleted_function_use : Error<"attempt to use a deleted function">; def err_deleted_inherited_ctor_use : Error< "constructor inherited by %0 from base class %1 is implicitly deleted">; Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -593,6 +593,10 @@ /// idea to avoid optimizing based on that undef behavior. virtual bool isCLZForZeroUndef() const { return true; } + /// \brief Check whether this target supports the __builtin_load_no_speculate + /// intrinsic. + virtual bool hasBuiltinLoadNoSpeculate() const { return false; } + /// \brief Returns the kind of __builtin_va_list type that should be used /// with this target. virtual BuiltinVaListKind getBuiltinVaListKind() const = 0; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -10337,6 +10337,7 @@ bool SemaBuiltinAssumeAligned(CallExpr *TheCall); bool SemaBuiltinLongjmp(CallExpr *TheCall); bool SemaBuiltinSetjmp(CallExpr *TheCall); + ExprResult SemaBuiltinLoadNoSpeculate(ExprResult TheCallResult); ExprResult SemaBuiltinAtomicOverloaded(ExprResult TheCallResult); ExprResult SemaBuiltinNontemporalOverloaded(ExprResult TheCallResult); ExprResult SemaAtomicOpsOverloaded(ExprResult TheCallResult, Index: lib/Basic/Targets/AArch64.h =================================================================== --- lib/Basic/Targets/AArch64.h +++ lib/Basic/Targets/AArch64.h @@ -67,6 +67,8 @@ CallingConvCheckResult checkCallingConvention(CallingConv CC) const override; + bool hasBuiltinLoadNoSpeculate() const override; + bool isCLZForZeroUndef() const override; BuiltinVaListKind getBuiltinVaListKind() const override; Index: lib/Basic/Targets/AArch64.cpp =================================================================== --- lib/Basic/Targets/AArch64.cpp +++ lib/Basic/Targets/AArch64.cpp @@ -263,6 +263,8 @@ bool AArch64TargetInfo::isCLZForZeroUndef() const { return false; } +bool AArch64TargetInfo::hasBuiltinLoadNoSpeculate() const { return true; } + TargetInfo::BuiltinVaListKind AArch64TargetInfo::getBuiltinVaListKind() const { return TargetInfo::AArch64ABIBuiltinVaList; } Index: lib/Basic/Targets/ARM.h =================================================================== --- lib/Basic/Targets/ARM.h +++ lib/Basic/Targets/ARM.h @@ -140,6 +140,8 @@ ArrayRef getTargetBuiltins() const override; + bool hasBuiltinLoadNoSpeculate() const override; + bool isCLZForZeroUndef() const override; BuiltinVaListKind getBuiltinVaListKind() const override; Index: lib/Basic/Targets/ARM.cpp =================================================================== --- lib/Basic/Targets/ARM.cpp +++ lib/Basic/Targets/ARM.cpp @@ -749,6 +749,17 @@ : TargetInfo::VoidPtrBuiltinVaList); } +bool ARMTargetInfo::hasBuiltinLoadNoSpeculate() const { + if (isThumb()) { + if (supportsThumb2()) { + if (ArchVersion >= 7) + return true; + } + } else + return true; + return false; +} + const char *const ARMTargetInfo::GCCRegNames[] = { // Integer registers "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -772,6 +772,73 @@ return Fn; } +/// @brief Utility to insert expansion for __builtin_load_no_speculate for +/// targets than don't support llvm.nospeculateload. Expand +/// __builtin_load_no_speculate(ptr, lower, upper, failval, cmpptr) +/// to a call to: +/// inline TYP __builtin_load_no_speculate +/// (const volatile TYP *ptr, +/// const volatile void *lower, +/// const volatile void *upper, +/// TYP failval, +/// const volatile void *cmpptr) +/// { +/// TYP result; +/// if (cmpptr >= lower && cmpptr < upper) +/// result = *ptr; +/// else +/// result = failval; +/// return result; +/// } +/// +static Value *MakeBuiltinLoadNoSpeculate(CodeGenFunction &CGF, Value *Ptr, + Value *LowerBound, Value *UpperBound, + Value *FailVal, Value *CmpPtr, + clang::QualType ResultType) { + Value *LowerBoundCmp = nullptr; + Value *UpperBoundCmp = nullptr; + Value *Condition = nullptr; + if (LowerBound) + LowerBoundCmp = CGF.Builder.CreateICmp(llvm::CmpInst::ICMP_UGE, CmpPtr, + LowerBound, "cmplower"); + if (UpperBound) + UpperBoundCmp = CGF.Builder.CreateICmp(llvm::CmpInst::ICMP_ULT, CmpPtr, + UpperBound, "cmpupper"); + if (LowerBoundCmp && UpperBoundCmp) + Condition = CGF.Builder.CreateAnd(LowerBoundCmp, UpperBoundCmp, "cond"); + else if (LowerBoundCmp) + Condition = LowerBoundCmp; + else { + assert(UpperBoundCmp != nullptr); + Condition = UpperBoundCmp; + } + + llvm::BasicBlock *InitialBB = CGF.Builder.GetInsertBlock(); + llvm::BasicBlock *InBoundsBB = + CGF.createBasicBlock("inBoundsBB", CGF.CurFn, InitialBB->getNextNode()); + llvm::BasicBlock *NextBB = + CGF.createBasicBlock("loadnospecJoinBB", CGF.CurFn, + InBoundsBB->getNextNode()); + + CGF.Builder.CreateCondBr(Condition, InBoundsBB, NextBB); + + CGF.Builder.SetInsertPoint(InBoundsBB); + Address PtrAddress(Ptr, + CGF.getContext().getTypeAlignInChars(ResultType)); + Value *PtrDeref = + CGF.Builder.CreateLoad(PtrAddress, true /*IsVolatile*/, "PtrDeref"); + CGF.Builder.CreateBr(NextBB); + + CGF.Builder.SetInsertPoint(NextBB); + llvm::PHINode *Phi = + CGF.Builder.CreatePHI(FailVal->getType(), 2, "load_no_speculate_res"); + Phi->addIncoming(PtrDeref, InBoundsBB); + Phi->addIncoming(FailVal, InitialBB); + + return Phi; +} + + RValue CodeGenFunction::emitBuiltinOSLogFormat(const CallExpr &E) { assert(E.getNumArgs() >= 2 && "__builtin_os_log_format takes at least 2 arguments"); @@ -3228,6 +3295,71 @@ Value *ArgPtr = Builder.CreateLoad(SrcAddr, "ap.val"); return RValue::get(Builder.CreateStore(ArgPtr, DestAddr)); } + case Builtin::BI__builtin_load_no_speculate: { + Value *Ptr = EmitScalarExpr(E->getArg(0)); + + bool LowerBoundNull = + E->getArg(1)->IgnoreParenCasts()->isNullPointerConstant( + getContext(), Expr::NPC_ValueDependentIsNotNull); + bool UpperBoundNull = + E->getArg(2)->IgnoreParenCasts()->isNullPointerConstant( + getContext(), Expr::NPC_ValueDependentIsNotNull); + + assert((!LowerBoundNull || !UpperBoundNull) && + "at least one bound must be non-null!"); + + Value *LowerBound = LowerBoundNull ? nullptr : EmitScalarExpr(E->getArg(1)); + Value *UpperBound = UpperBoundNull ? nullptr : EmitScalarExpr(E->getArg(2)); + + llvm::Type *T = ConvertType(E->getType()); + assert((isa(T) || isa(T)) && + "unsupported type"); + + if (isa(T)) { + CharUnits LoadSize = getContext().getTypeSizeInChars(E->getType()); + T = llvm::IntegerType::get(getLLVMContext(), LoadSize.getQuantity() * 8); + Ptr = Builder.CreateBitCast(Ptr, T->getPointerTo()); + } + + // If failval is omitted, zero of the appropriate type will be used. + Value *FailVal = nullptr; + if (E->getNumArgs() >= 4) { + FailVal = EmitScalarExpr(E->getArg(3)); + if (T != FailVal->getType()) { + assert(T->getScalarSizeInBits() > + FailVal->getType()->getScalarSizeInBits()); + FailVal = Builder.CreateZExt(FailVal, T); + } + } else { + if (isa(T)) { + FailVal = llvm::ConstantInt::get(T, 0); + } else if (isa(T)) { + FailVal = llvm::ConstantPointerNull::get(cast(T)); + } + } + assert(FailVal && "default FailVal not set"); + + // If cmpptr is omitted, ptr will be used. + Value *CmpPtr = (E->getNumArgs() >= 5) ? EmitScalarExpr(E->getArg(4)) : Ptr; + CmpPtr = Builder.CreateBitCast(CmpPtr, Builder.getInt8PtrTy()); + + if (!getContext().getTargetInfo().hasBuiltinLoadNoSpeculate()) + return RValue::get(MakeBuiltinLoadNoSpeculate( + *this, Ptr, LowerBound, UpperBound, FailVal, CmpPtr, + E->getType())); + else if (LowerBoundNull) + return RValue::get(Builder.CreateCall( + CGM.getIntrinsic(Intrinsic::nospeculateload_nolower, T), + {Ptr, UpperBound, FailVal, CmpPtr})); + else if (UpperBoundNull) + return RValue::get(Builder.CreateCall( + CGM.getIntrinsic(Intrinsic::nospeculateload_noupper, T), + {Ptr, LowerBound, FailVal, CmpPtr})); + else + return RValue::get( + Builder.CreateCall(CGM.getIntrinsic(Intrinsic::nospeculateload, T), + {Ptr, LowerBound, UpperBound, FailVal, CmpPtr})); + } } // If this is an alias for a lib function (e.g. __builtin_sin), emit Index: lib/Frontend/InitPreprocessor.cpp =================================================================== --- lib/Frontend/InitPreprocessor.cpp +++ lib/Frontend/InitPreprocessor.cpp @@ -1060,6 +1060,8 @@ Builder.defineMacro("__GLIBCXX_BITSIZE_INT_N_0", "128"); } + Builder.defineMacro("__HAVE_LOAD_NO_SPECULATE"); + // Get other target #defines. TI.getTargetDefines(LangOpts, Builder); } Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -1226,6 +1226,8 @@ if (SemaBuiltinOSLogFormat(TheCall)) return ExprError(); break; + case Builtin::BI__builtin_load_no_speculate: + return SemaBuiltinLoadNoSpeculate(TheCallResult); } // Since the target specific builtins for each arch overlap, only check those @@ -3335,7 +3337,7 @@ InitializedEntity Entity = InitializedEntity::InitializeParameter(S.Context, Param); - ExprResult Arg = E->getArg(0); + ExprResult Arg = E->getArg(ArgIndex); Arg = S.PerformCopyInitialization(Entity, SourceLocation(), Arg); if (Arg.isInvalid()) return true; @@ -3784,6 +3786,107 @@ return TheCallResult; } +ExprResult Sema::SemaBuiltinLoadNoSpeculate(ExprResult TheCallResult) { + CallExpr *TheCall = (CallExpr *)TheCallResult.get(); + DeclRefExpr *DRE = + cast(TheCall->getCallee()->IgnoreParenCasts()); + FunctionDecl *FDecl = cast(DRE->getDecl()); + unsigned BuiltinID = FDecl->getBuiltinID(); + assert(BuiltinID == Builtin::BI__builtin_load_no_speculate && + "Unexpected no_speculate_val builtin!"); + + // Too few args. + if (TheCall->getNumArgs() < 3) + return Diag(TheCall->getLocEnd(), diag::err_typecheck_call_too_few_args_at_least) + << 0 /*function call*/ << 3 /* min args */ << TheCall->getNumArgs(); + + // Too many args. + if (TheCall->getNumArgs() > 5) + return Diag(TheCall->getArg(5)->getLocStart(), + diag::err_typecheck_call_too_many_args_at_most) + << 0 /*function call*/ << 5 << TheCall->getNumArgs() + << SourceRange(TheCall->getArg(5)->getLocStart(), + (*(TheCall->arg_end()-1))->getLocEnd()); + + // Derive the return type from the pointer argument. + ExprResult FirstArg = DefaultFunctionArrayLvalueConversion(TheCall->getArg(0)); + if (FirstArg.isInvalid()) + return true; + TheCall->setArg(0, FirstArg.get()); + QualType FirstArgType = FirstArg.get()->getType(); + + // The first argument must be a pointer type. + if (!FirstArgType->isAnyPointerType()) + return Diag( + TheCall->getArg(0)->getLocStart(), + diag:: + err_nospeculateload_builtin_must_be_pointer_to_pointer_or_integral) + << TheCall->getArg(0)->getType() + << TheCall->getArg(0)->getSourceRange(); + + QualType RetType = FirstArgType->getPointeeType(); + TheCall->setType(RetType); + + // The first argument must be a pointer to an integer or pointer type. + if (!(RetType->isIntegerType() || RetType->isAnyPointerType())) + return Diag( + TheCall->getArg(0)->getLocStart(), + diag:: + err_nospeculateload_builtin_must_be_pointer_to_pointer_or_integral) + << TheCall->getArg(0)->getType() + << TheCall->getArg(0)->getSourceRange(); + + // The lower and upper bounds must be pointers. + if (checkBuiltinArgument(*this, TheCall, 1)) + return true; + if (checkBuiltinArgument(*this, TheCall, 2)) + return true; + + // The lower and upper bounds must not both be null. + bool LowerBoundNull = + TheCall->getArg(1)->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); + bool UpperBoundNull = + TheCall->getArg(2)->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); + if (LowerBoundNull && UpperBoundNull) { + return Diag(DRE->getLocStart(), + diag::err_nospeculateload_builtin_bounds_cannot_both_be_null) + << TheCall->getArg(1)->getSourceRange() + << TheCall->getArg(2)->getSourceRange(); + } + + // If the failval argument is provided, it must be compatible with the type + // being loaded. + if (TheCall->getNumArgs() >= 4) { + Expr *ValArg = TheCall->getArg(3); + ExprResult ValArgResult = DefaultFunctionArrayLvalueConversion(ValArg); + AssignConvertType ConvTy = + CheckSingleAssignmentConstraints(RetType, ValArgResult); + + if (ValArgResult.isInvalid()) + return ExprError(); + if (DiagnoseAssignmentResult(ConvTy, TheCall->getArg(3)->getLocStart(), + RetType, ValArg->getType(), ValArg, + AA_Passing)) + return true; + TheCall->setArg(3, ValArgResult.get()); + } + + // If provided, the cmpptr argument must be a pointer. + if (TheCall->getNumArgs() >= 5) + if (checkBuiltinArgument(*this, TheCall, 4)) + return true; + + // If the target doesn't support the non-speculation semantics of the + // intrinsic, issue a warning. + if (!Context.getTargetInfo().hasBuiltinLoadNoSpeculate()) + Diag(DRE->getLocStart(), + diag::warn_nospeculateload_builtin_target_not_supported); + + return TheCallResult; +} + /// CheckObjCString - Checks that the argument to the builtin /// CFString constructor is correct /// Note: It might also make sense to do the UTF-16 conversion here (would Index: test/CodeGen/builtin-load-no-speculate.c =================================================================== --- /dev/null +++ test/CodeGen/builtin-load-no-speculate.c @@ -0,0 +1,122 @@ +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm %s -o - | FileCheck -check-prefix=CHECK-SUPPORTED %s +// RUN: %clang_cc1 -triple thumbv8m.baseline -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-EXPANDED %s + +void test_load_widths(void *ptr, void *lower, void *upper) { +#ifdef __AARCH64EL__ + // CHECK-LABEL-SUPPORTED: define void @test_load_widths + + __builtin_load_no_speculate((_Bool*)ptr, lower, upper); + // CHECK-SUPPORTED: call i8 @llvm.nospeculateload.i8(i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8 0, i8* %{{[0-9a-z]+}}) + + _Bool FailVal; + __builtin_load_no_speculate((_Bool*)ptr, lower, upper, FailVal); + // CHECK-SUPPORTED: %[[FAILVALEXT:[0-9a-zA-Z]+]] = zext i1 %{{[0-9a-z]+}} to i8 + // CHECK-SUPPORTED: call i8 @llvm.nospeculateload.i8(i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8 %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}) + + + __builtin_load_no_speculate((char*)ptr, lower, upper); + // CHECK-SUPPORTED: call i8 @llvm.nospeculateload.i8(i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8 0, i8* %{{[0-9a-z]+}}) + + __builtin_load_no_speculate((short*)ptr, lower, upper); + // CHECK-SUPPORTED: call i16 @llvm.nospeculateload.i16(i16* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i16 0, i8* %{{[0-9a-z]+}}) + + __builtin_load_no_speculate((int*)ptr, lower, upper); + // CHECK-SUPPORTED: call i32 @llvm.nospeculateload.i32(i32* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i32 0, i8* %{{[0-9a-z]+}}) + + __builtin_load_no_speculate((long*)ptr, lower, upper); + // CHECK-SUPPORTED: call i64 @llvm.nospeculateload.i64(i64* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i64 0, i8* %{{[0-9a-z]+}}) + + __builtin_load_no_speculate((__int128*)ptr, lower, upper); + // CHECK-SUPPORTED: call i128 @llvm.nospeculateload.i128(i128* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i128 0, i8* %{{[0-9a-z]+}}) + + __builtin_load_no_speculate((int**)ptr, lower, upper); + // CHECK-SUPPORTED: call i32* @llvm.nospeculateload.p0i32(i32** %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i32* null, i8* %{{[0-9a-z]+}}) + + int arr[4]; + __builtin_load_no_speculate(arr, lower, upper); + // CHECK-SUPPORTED: call i32 @llvm.nospeculateload.i32(i32* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i32 0, i8* %{{[0-9a-z]+}}) +#endif +} + +void test_default_args(int *ptr, void *lower, void *upper, int failval, void *cmpptr) { + __builtin_load_no_speculate((int*)ptr, lower, upper); + // CHECK-SUPPORTED: %[[CMPPTR:[0-9a-z]+]] = bitcast i32* %[[PTR:[0-9a-z]+]] to i8* + // CHECK-SUPPORTED: call i32 @llvm.nospeculateload.i32(i32* %[[PTR]], i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i32 0, i8* %[[CMPPTR]]) + // + // CHECK-EXPANDED: %[[CMPPTR:[0-9a-z]+]] = bitcast i32* %[[PTR:[0-9a-z]+]] to i8* + // CHECK-EXPANDED: %cmplower = icmp uge i8* %[[CMPPTR]], %[[LOWER:[0-9a-z]+]] + // CHECK-EXPANDED-NEXT: %cmpupper = icmp ult i8* %[[CMPPTR]], %[[UPPER:[0-9a-z]+]] + // CHECK-EXPANDED-NEXT: %cond = and i1 %cmplower, %cmpupper + // CHECK-EXPANDED-NEXT: br i1 %cond, label %[[INBOUNDSBB:[0-9a-zA-Z]+]], label %[[NEXTBB:[0-9a-zA-Z]+]] + + // CHECK-EXPANDED: [[INBOUNDSBB]]: ; preds = %entry + // CHECK-EXPANDED-NEXT: %PtrDeref = load volatile i32, i32* %[[PTR]], align 4 + // CHECK-EXPANDED-NEXT: br label %[[NEXTBB:[0-9a-zA-Z]+]] + + // CHECK-EXPANDED: [[NEXTBB]]: ; preds = %[[INBOUNDSBB]], %entry + // CHECK-EXPANDED-NEXT: %load_no_speculate_res = phi i32 [ %PtrDeref, %[[INBOUNDSBB]] ], [ 0, %entry ] + + __builtin_load_no_speculate((int*)ptr, lower, upper, 42); + // CHECK-SUPPORTED: %[[CMPPTR:[0-9a-z]+]] = bitcast i32* %[[PTR:[0-9a-z]+]] to i8* + // CHECK-SUPPORTED: call i32 @llvm.nospeculateload.i32(i32* %[[PTR]], i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i32 42, i8* %[[CMPPTR]]) + + // CHECK-EXPANDED: %[[CMPLOWER:cmplower[0-9]*]] = icmp uge i8* %[[CMPPTR:[0-9a-z]+]], %[[LOWER:[0-9a-z]+]] + // CHECK-EXPANDED-NEXT: %[[CMPUPPER:cmpupper[0-9]*]] = icmp ult i8* %[[CMPPTR]], %[[UPPER:[0-9a-z]+]] + // CHECK-EXPANDED-NEXT: %[[COND:cond[0-9]*]] = and i1 %[[CMPLOWER]], %[[CMPUPPER]] + // CHECK-EXPANDED-NEXT: br i1 %[[COND]], label %[[INBOUNDSBB:[0-9a-zA-Z]+]], label %[[NEXTBB2:[0-9a-zA-Z]+]] + + // CHECK-EXPANDED: [[INBOUNDSBB]]: ; preds = %[[NEXTBB]] + // CHECK-EXPANDED-NEXT: %[[PTRDEREF:PtrDeref[0-9]*]] = load volatile i32, i32* %[[PTR:[0-9a-z]+]], align 4 + // CHECK-EXPANDED-NEXT: br label %[[NEXTBB2]] + + // CHECK-EXPANDED: [[NEXTBB2]]: ; preds = %[[INBOUNDSBB]], %[[NEXTBB]] + // CHECK-EXPANDED-NEXT: %load_no_speculate_res{{[0-9*]}} = phi i32 [ %[[PTRDEREF]], %[[INBOUNDSBB]] ], [ 42, %[[NEXTBB]] ] + + __builtin_load_no_speculate((int*)ptr, lower, upper, 42, cmpptr); + // CHECK-SUPPORTED-NOT: bitcast + // CHECK-SUPPORTED: call i32 @llvm.nospeculateload.i32(i32* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i32 42, i8* %{{[0-9a-z]+}}) + + // CHECK-EXPANDED-NOT: bitcast + // CHECK-EXPANDED: %[[CMPLOWER:cmplower[0-9]*]] = icmp uge i8* %[[CMPPTR:[0-9a-z]+]], %[[LOWER:[0-9a-z]+]] + // CHECK-EXPANDED-NEXT: %[[CMPUPPER:cmpupper[0-9]*]] = icmp ult i8* %[[CMPPTR]], %[[UPPER:[0-9a-z]+]] + // CHECK-EXPANDED-NEXT: %[[COND:cond[0-9]*]] = and i1 %[[CMPLOWER]], %[[CMPUPPER]] + // CHECK-EXPANDED-NEXT: br i1 %[[COND]], label %[[INBOUNDSBB3:[0-9a-zA-Z]+]], label %[[NEXTBB3:[0-9a-zA-Z]+]] + + // CHECK-EXPANDED: [[INBOUNDSBB3]]: ; preds = %[[NEXTBB2]] + // CHECK-EXPANDED-NEXT: %[[PTRDEREF:PtrDeref[0-9]*]] = load volatile i32, i32* %[[PTR:[0-9a-z]+]], align 4 + // CHECK-EXPANDED-NEXT: br label %[[NEXTBB3]] + + // CHECK-EXPANDED: [[NEXTBB3]]: ; preds = %[[INBOUNDSBB3]], %[[NEXTBB2]] + // CHECK-EXPANDED-NEXT: %load_no_speculate_res{{[0-9]*}} = phi i32 [ %[[PTRDEREF]], %[[INBOUNDSBB3]] ], [ 42, %[[NEXTBB2]] ] +} + +void test_nolower_noupper(int *ptr, void *lower, void *upper, int failval, void *cmpptr) { + __builtin_load_no_speculate((int*)ptr, 0, upper, 42, cmpptr); + // CHECK-SUPPORTED-NOT: bitcast + // CHECK-SUPPORTED: call i32 @llvm.nospeculateload.nolower.i32(i32* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i32 42, i8* %{{[0-9a-z]+}}) + + // CHECK-EXPANDED-NOT: bitcast + // CHECK-EXPANDED: %[[CMPUPPER:cmpupper[0-9]*]] = icmp ult i8* %[[CMPPTR:[0-9a-z]+]], %[[UPPER:[0-9a-z]+]] + // CHECK-EXPANDED-NEXT: br i1 %[[CMPUPPER]], label %[[INBOUNDSBB3:[0-9a-zA-Z]+]], label %[[NEXTBB3:[0-9a-zA-Z]+]] + + // CHECK-EXPANDED: [[INBOUNDSBB3]]: ; preds = %entry + // CHECK-EXPANDED-NEXT: %[[PTRDEREF:PtrDeref[0-9]*]] = load volatile i32, i32* %[[PTR:[0-9a-z]+]], align 4 + // CHECK-EXPANDED-NEXT: br label %[[NEXTBB3]] + + // CHECK-EXPANDED: [[NEXTBB3]]: ; preds = %[[INBOUNDSBB3]], %entry + // CHECK-EXPANDED-NEXT: %load_no_speculate_res{{[0-9]*}} = phi i32 [ %[[PTRDEREF]], %[[INBOUNDSBB3]] ], [ 42, %entry ] + __builtin_load_no_speculate((int*)ptr, lower, 0, 42, cmpptr); + // CHECK-SUPPORTED-NOT: bitcast + // CHECK-SUPPORTED: call i32 @llvm.nospeculateload.noupper.i32(i32* %{{[0-9a-z]+}}, i8* %{{[0-9a-z]+}}, i32 42, i8* %{{[0-9a-z]+}}) + + // CHECK-EXPANDED-NOT: bitcast + // CHECK-EXPANDED: %[[CMPLOWER:cmplower[0-9]*]] = icmp uge i8* %[[CMPPTR:[0-9a-z]+]], %[[LOWER:[0-9a-z]+]] + // CHECK-EXPANDED-NEXT: br i1 %[[CMPLOWER]], label %[[INBOUNDSBB4:[0-9a-zA-Z]+]], label %[[NEXTBB4:[0-9a-zA-Z]+]] + + // CHECK-EXPANDED: [[INBOUNDSBB4]]: ; preds = %[[NEXTBB3]] + // CHECK-EXPANDED-NEXT: %[[PTRDEREF:PtrDeref[0-9]*]] = load volatile i32, i32* %[[PTR:[0-9a-z]+]], align 4 + // CHECK-EXPANDED-NEXT: br label %[[NEXTBB4]] + + // CHECK-EXPANDED: [[NEXTBB4]]: ; preds = %[[INBOUNDSBB4]], %[[NEXTBB3]] + // CHECK-EXPANDED-NEXT: %load_no_speculate_res{{[0-9]*}} = phi i32 [ %[[PTRDEREF]], %[[INBOUNDSBB4]] ], [ 42, %[[NEXTBB3]] ] +} Index: test/Preprocessor/init.c =================================================================== --- test/Preprocessor/init.c +++ test/Preprocessor/init.c @@ -96,6 +96,7 @@ // COMMON:#define __GNUC_STDC_INLINE__ 1 // COMMON:#define __GNUC__ {{.*}} // COMMON:#define __GXX_ABI_VERSION {{.*}} +// COMMON:#define __HAVE_LOAD_NO_SPECULATE 1 // COMMON:#define __ORDER_BIG_ENDIAN__ 4321 // COMMON:#define __ORDER_LITTLE_ENDIAN__ 1234 // COMMON:#define __ORDER_PDP_ENDIAN__ 3412 @@ -9134,6 +9135,7 @@ // WEBASSEMBLY32-NEXT:#define __GNUC_STDC_INLINE__ 1 // WEBASSEMBLY32-NEXT:#define __GNUC__ {{.*}} // WEBASSEMBLY32-NEXT:#define __GXX_ABI_VERSION 1002 +// WEBASSEMBLY32-NEXT:#define __HAVE_LOAD_NO_SPECULATE 1 // WEBASSEMBLY32-NEXT:#define __ILP32__ 1 // WEBASSEMBLY32-NEXT:#define __INT16_C_SUFFIX__ // WEBASSEMBLY32-NEXT:#define __INT16_FMTd__ "hd" @@ -9466,6 +9468,7 @@ // WEBASSEMBLY64-NEXT:#define __GNUC_STDC_INLINE__ 1 // WEBASSEMBLY64-NEXT:#define __GNUC__ {{.}} // WEBASSEMBLY64-NEXT:#define __GXX_ABI_VERSION 1002 +// WEBASSEMBLY64-NEXT:#define __HAVE_LOAD_NO_SPECULATE 1 // WEBASSEMBLY64-NOT:#define __ILP32__ // WEBASSEMBLY64-NEXT:#define __INT16_C_SUFFIX__ // WEBASSEMBLY64-NEXT:#define __INT16_FMTd__ "hd" Index: test/Sema/builtin-load-no-speculate-c.c =================================================================== --- /dev/null +++ test/Sema/builtin-load-no-speculate-c.c @@ -0,0 +1,113 @@ +// REQUIRES: arm-registered-target +// REQUIRES: aarch64-registered-target +// RUN: %clang_cc1 -triple aarch64 -DENABLE_ERRORS -verify %s +// RUN: %clang_cc1 -triple armv7a -DENABLE_ERRORS -verify %s +// RUN: %clang_cc1 -triple aarch64 %s -emit-llvm -o - +// RUN: %clang_cc1 -triple armv7a %s -emit-llvm -o - + +void test_valid(int *ptr, int *lower, int *upper, int failval, int *cmpptr) { + __builtin_load_no_speculate(ptr, lower, upper); + __builtin_load_no_speculate(ptr, lower, upper, failval); + __builtin_load_no_speculate(ptr, lower, upper, failval, cmpptr); +} + +void test_null_bounds(int *ptr, int *lower, int *upper, int failval, int *cmpptr) { + __builtin_load_no_speculate(ptr, lower, 0); + __builtin_load_no_speculate(ptr, lower, (void*)0); + +#ifdef ENABLE_ERRORS + __builtin_load_no_speculate(ptr, 0, 0); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} + __builtin_load_no_speculate(ptr, (void*)0, (void*)0); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} + __builtin_load_no_speculate(ptr, 0, (void*)0); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} + __builtin_load_no_speculate(ptr, 0, 0, failval, cmpptr); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} +#endif +} + +void test_load_type(void *lower, void *upper) { + char c; + c = __builtin_load_no_speculate(&c, lower, upper); + + short s; + s = __builtin_load_no_speculate(&s, lower, upper); + + int i; + i = __builtin_load_no_speculate(&i, lower, upper); + + long l; + l = __builtin_load_no_speculate(&l, lower, upper); + + long long ll; + ll = __builtin_load_no_speculate(&ll, lower, upper); + + int *ip; + ip = __builtin_load_no_speculate(&ip, lower, upper); + + int (*fp)(int, int); + fp = __builtin_load_no_speculate(&fp, lower, upper); + +#ifdef ENABLE_ERRORS + i = __builtin_load_no_speculate(i, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('int' invalid)}} + + float f; + f = __builtin_load_no_speculate(&f, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('float *' invalid)}} + + struct S { int a; } S; + S = __builtin_load_no_speculate(&S, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('struct S *' invalid)}} + + union U { int a; } U; + U = __builtin_load_no_speculate(&U, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('union U *' invalid)}} + + char __attribute__((vector_size(16))) v; + v = __builtin_load_no_speculate(&v, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('__attribute__((__vector_size__(16 * sizeof(char)))) char *' invalid)}} +#endif +} + +void test_bounds_types(void *lower, void *upper, void *cmpptr) { + int i; + + __builtin_load_no_speculate(&i, 1234, upper); // expected-warning {{incompatible integer to pointer conversion passing 'int' to parameter of type 'const volatile void *'}} + __builtin_load_no_speculate(&i, lower, 5678); // expected-warning {{incompatible integer to pointer conversion passing 'int' to parameter of type 'const volatile void *'}} + __builtin_load_no_speculate(&i, lower, upper, 0, 5678); // expected-warning {{incompatible integer to pointer conversion passing 'int' to parameter of type 'const volatile void *'}} + +#ifdef ENABLE_ERRORS + __builtin_load_no_speculate(&i, 3.141, upper); // expected-error {{passing 'double' to parameter of incompatible type 'const volatile void *'}} + __builtin_load_no_speculate(&i, lower, 3.141); // expected-error {{passing 'double' to parameter of incompatible type 'const volatile void *'}} + __builtin_load_no_speculate(&i, lower, upper, 0, 3.141); // expected-error {{passing 'double' to parameter of incompatible type 'const volatile void *'}} +#endif + + // The bounds pointers are of type 'void *', the pointers do not have to be + // compatible with the first arg. + __builtin_load_no_speculate(&i, (short*)lower, (int(*)(int))upper, 0, (float*)0); + + __builtin_load_no_speculate(&i, (const int *)lower, upper); + __builtin_load_no_speculate(&i, (volatile float *)lower, upper); + __builtin_load_no_speculate(&i, lower, (const volatile char *)upper); + __builtin_load_no_speculate(&i, lower, upper, 0, (const volatile char *)cmpptr); +} + +void test_failval_type(void *lower, void *upper) { + char c; + int i; + int *ip; + int * const cip; + int * volatile vip; + char *cp; + int (*fp)(int); + + // Exactly correct type + __builtin_load_no_speculate(&i, lower, upper, 0); + __builtin_load_no_speculate(&i, lower, upper, i); + + // Implicitly convertible type + __builtin_load_no_speculate(&i, lower, upper, (char)0); + __builtin_load_no_speculate((char*)&c, (char*)lower, (char*)upper, (int)0); + __builtin_load_no_speculate(&i, lower, upper, 0.0); + __builtin_load_no_speculate(&ip, lower, upper, cip); + __builtin_load_no_speculate(&ip, lower, upper, vip); + + __builtin_load_no_speculate(&i, lower, upper, ip); // expected-warning {{incompatible pointer to integer conversion passing 'int *' to parameter of type 'int'; dereference with *}} + __builtin_load_no_speculate(&ip, lower, upper, i); // expected-warning {{incompatible integer to pointer conversion passing 'int' to parameter of type 'int *'; take the address with &}} + __builtin_load_no_speculate(&ip, lower, upper, cp); // expected-warning {{incompatible pointer types passing 'char *' to parameter of type 'int *'}} + __builtin_load_no_speculate(&ip, lower, upper, fp); // expected-warning {{incompatible pointer types passing 'int (*)(int)' to parameter of type 'int *'}} + __builtin_load_no_speculate(&fp, lower, upper, ip); // expected-warning {{incompatible pointer types passing 'int *' to parameter of type 'int (*)(int)'}} +} Index: test/Sema/builtin-load-no-speculate-cxx.cpp =================================================================== --- /dev/null +++ test/Sema/builtin-load-no-speculate-cxx.cpp @@ -0,0 +1,144 @@ +// REQUIRES: arm-registered-target +// REQUIRES: aarch64-registered-target +// RUN: %clang_cc1 -triple aarch64 -x c++ -std=c++11 -DENABLE_ERRORS -verify %s +// RUN: %clang_cc1 -triple armv7a -x c++ -std=c++11 -DENABLE_ERRORS -verify %s +// RUN: %clang_cc1 -triple aarch64 -x c++ -std=c++11 %s -emit-llvm -o - +// RUN: %clang_cc1 -triple armv7a -x c++ -std=c++11 %s -emit-llvm -o - + +void test_valid(int *ptr, int *lower, int *upper, int failval, int *cmpptr) { + __builtin_load_no_speculate(ptr, lower, upper); + __builtin_load_no_speculate(ptr, lower, upper, failval); + __builtin_load_no_speculate(ptr, lower, upper, failval, cmpptr); +} + +void test_null_bounds(int *ptr, int *lower, int *upper, int failval, int *cmpptr) { + __builtin_load_no_speculate(ptr, lower, 0); + __builtin_load_no_speculate(ptr, lower, (void*)0); + +#ifdef ENABLE_ERRORS + __builtin_load_no_speculate(ptr, 0, 0); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} + __builtin_load_no_speculate(ptr, (void*)0, (void*)0); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} + __builtin_load_no_speculate(ptr, 0, (void*)0); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} + __builtin_load_no_speculate(ptr, 0, 0, failval, cmpptr); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} +#endif + + __builtin_load_no_speculate(ptr, lower, nullptr); + __builtin_load_no_speculate(ptr, nullptr, upper); +#ifdef ENABLE_ERRORS + __builtin_load_no_speculate(ptr, nullptr, nullptr); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} + __builtin_load_no_speculate(ptr, 0, nullptr); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} + __builtin_load_no_speculate(ptr, nullptr, (void*)0); // expected-error {{lower and upper bounds arguments to load_no_speculate builtin must not both be null}} +#endif +} + +void test_load_type(void *lower, void *upper) { + char c; + c = __builtin_load_no_speculate(&c, lower, upper); + + short s; + s = __builtin_load_no_speculate(&s, lower, upper); + + int i; + i = __builtin_load_no_speculate(&i, lower, upper); + + long l; + l = __builtin_load_no_speculate(&l, lower, upper); + + long long ll; + ll = __builtin_load_no_speculate(&ll, lower, upper); + + int *ip; + ip = __builtin_load_no_speculate(&ip, lower, upper); + + int (*fp)(int, int); + fp = __builtin_load_no_speculate(&fp, lower, upper); + +#ifdef ENABLE_ERRORS + i = __builtin_load_no_speculate(i, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('int' invalid)}} + + float f; + f = __builtin_load_no_speculate(&f, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('float *' invalid)}} + + struct S { int a; } S; + S = __builtin_load_no_speculate(&S, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('struct S *' invalid)}} + + union U { int a; } U; + U = __builtin_load_no_speculate(&U, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('union U *' invalid)}} + + char __attribute__((vector_size(16))) v; + v = __builtin_load_no_speculate(&v, lower, upper); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('__attribute__((__vector_size__(16 * sizeof(char)))) char *' invalid)}} +#endif +} + +void test_bounds_types(void *lower, void *upper, void *cmpptr) { + int i; + +#ifdef ENABLE_ERRORS + __builtin_load_no_speculate(&i, 1234, upper); // expected-error {{cannot initialize a parameter of type 'const volatile void *' with an rvalue of type 'int'}} + __builtin_load_no_speculate(&i, lower, 5678); // expected-error {{cannot initialize a parameter of type 'const volatile void *' with an rvalue of type 'int}} + __builtin_load_no_speculate(&i, lower, upper, 0, 5678); // expected-error {{cannot initialize a parameter of type 'const volatile void *' with an rvalue of type 'int}} + + __builtin_load_no_speculate(&i, 3.141, upper); // expected-error {{cannot initialize a parameter of type 'const volatile void *' with an rvalue of type 'double'}} + __builtin_load_no_speculate(&i, lower, 3.141); // expected-error {{cannot initialize a parameter of type 'const volatile void *' with an rvalue of type 'double'}} + __builtin_load_no_speculate(&i, lower, upper, 0, 3.141); // expected-error {{cannot initialize a parameter of type 'const volatile void *' with an rvalue of type 'double'}} +#endif + + // The bounds pointers are of type 'void *', the pointers do not have to be + // compatible with the first arg. + struct S {}; + __builtin_load_no_speculate(&i, (short *)lower, (S *)upper, 0, (float *)0); + + __builtin_load_no_speculate(&i, (const int *)lower, upper); + __builtin_load_no_speculate(&i, (volatile float *)lower, upper); + __builtin_load_no_speculate(&i, lower, (const volatile char *)upper); + __builtin_load_no_speculate(&i, lower, upper, 0, (const volatile char *)cmpptr); +} + +void test_failval_type(void *lower, void *upper) { + char c; + int i; + int *ip; + int * const cip = 0; + int * volatile vip; + char *cp; + int (*fp)(int); + + // Exactly correct type + __builtin_load_no_speculate(&i, lower, upper, 0); + __builtin_load_no_speculate(&i, lower, upper, i); + + // Implicitly convertible type + __builtin_load_no_speculate(&i, lower, upper, (char)0); + __builtin_load_no_speculate(&i, lower, upper, 0.0); + __builtin_load_no_speculate(&ip, lower, upper, cip); + __builtin_load_no_speculate(&ip, lower, upper, vip); + +#ifdef ENABLE_ERRORS + __builtin_load_no_speculate(&i, lower, upper, ip); // expected-error {{assigning to 'int' from incompatible type 'int *'; dereference with *}} + __builtin_load_no_speculate(&ip, lower, upper, i); // expected-error {{assigning to 'int *' from incompatible type 'int'; take the address with &}} + __builtin_load_no_speculate(&ip, lower, upper, cp); // expected-error {{assigning to 'int *' from incompatible type 'char *'}} + __builtin_load_no_speculate(&ip, lower, upper, fp); // expected-error {{assigning to 'int *' from incompatible type 'int (*)(int)'}} + __builtin_load_no_speculate(&fp, lower, upper, ip); // expected-error {{assigning to 'int (*)(int)' from incompatible type 'int *'}} +#endif +} + +#ifdef ENABLE_ERRORS +template +T load(const T *ptr, const T *upper, const T *lower) { + return __builtin_load_no_speculate(ptr, upper, lower); // expected-error {{argument to load_no_speculate builtin must be a pointer to a pointer or integer ('const S *' invalid)}} +} + +void test_templates() { + int ia[5]; + load(&ia[3], &ia[0], &ia[5]); + + struct S { int a; }; + S Sa[5]; + load(&Sa[3], &Sa[0], &Sa[5]); // expected-note {{in instantiation of function template specialization 'load' requested here}} +} +#endif + +void test_convert_failval_type() { + bool foo; + __builtin_load_no_speculate(&foo, (void *)0x1000, (void *)0x2000, false); +} Index: test/Sema/builtin-load-no-speculate-target-not-supported.c =================================================================== --- /dev/null +++ test/Sema/builtin-load-no-speculate-target-not-supported.c @@ -0,0 +1,6 @@ +// REQUIRES: arm-registered-target +// RUN: %clang_cc1 -triple thumbv8m.baseline -fsyntax-only -verify %s + +void test_valid(int *ptr, int *lower, int *upper, int failval, int *cmpptr) { + __builtin_load_no_speculate(ptr, lower, upper, failval, cmpptr); // expected-warning {{this target does not support anti-speculation operations. Your program will still execute correctly, but speculation will not be inhibited}} +}