Index: lib/Analysis/NaCl/PNaClABIVerifyModule.cpp |
diff --git a/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp b/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp |
index 7836484c2f868e02590d9507e11c6dd72c69ac33..48fe8baeeb7b722d604e1a817234febd01c9e0dc 100644 |
--- a/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp |
+++ b/lib/Analysis/NaCl/PNaClABIVerifyModule.cpp |
@@ -13,13 +13,15 @@ |
// |
//===----------------------------------------------------------------------===// |
-#include "llvm/Pass.h" |
#include "llvm/ADT/Twine.h" |
#include "llvm/Analysis/NaCl.h" |
#include "llvm/IR/Constants.h" |
#include "llvm/IR/DerivedTypes.h" |
+#include "llvm/IR/Instructions.h" |
#include "llvm/IR/Intrinsics.h" |
#include "llvm/IR/Module.h" |
+#include "llvm/IR/NaCl.h" |
+#include "llvm/Pass.h" |
#include "llvm/Support/Debug.h" |
#include "llvm/Support/raw_ostream.h" |
@@ -181,12 +183,81 @@ static bool isWhitelistedCountBits(const Function *F, unsigned num_params) { |
return TypeAcceptable(ParamType, AcceptableTypes); |
} |
+// The signature for atomics is: |
+// T nacl.atomic.<size>(int32_t operation, T *location, T value, |
+// T old_value, int32_t memory_order); |
+// With: |
+// - T in {i8, i16, i32, i64}. |
+// - operation as a valid NaCl::AtomicOperation. |
+// - memory_order as a valid NaCl::MemoryOrder. |
+static bool isWhitelistedAtomic(const Function *F, unsigned ID) { |
+ LLVMContext &C = F->getContext(); |
+ FunctionType *FT = F->getFunctionType(); |
+ |
+ Type *ReturnType = FT->getReturnType(); |
+ switch (ID) { |
+ default: llvm_unreachable("unhandled atomic intrinsic"); |
+ case Intrinsic::nacl_atomic_8: |
+ if (ReturnType != Type::getInt8Ty(C)) return false; |
eliben
2013/06/26 16:20:57
Let's separate type checking from an intrinsic bei
JF
2013/06/26 22:23:12
Done.
|
+ break; |
+ case Intrinsic::nacl_atomic_16: |
Mark Seaborn
2013/06/26 14:33:41
I don't think we have validator support for 16-bit
JF
2013/06/26 15:52:29
Vlad thinks it can be in for M30. Presumably this
Mark Seaborn
2013/06/26 16:47:01
I don't think we should block PNaCl ABI stability
JF
2013/06/26 22:56:36
Agreed, and this in no way blocks PNaCl ABI stabil
Mark Seaborn
2013/06/27 01:04:33
I'm not suggesting *permanently* penalizing pexes.
JF
2013/06/27 01:31:39
16-bit atomics are supported on all architectures.
|
+ if (ReturnType != Type::getInt16Ty(C)) return false; |
+ break; |
+ case Intrinsic::nacl_atomic_32: |
+ if (ReturnType != Type::getInt32Ty(C)) return false; |
+ break; |
+ case Intrinsic::nacl_atomic_64: |
Mark Seaborn
2013/06/26 14:33:41
I think we should omit 64-bit atomics, because MIP
JF
2013/06/26 15:52:29
Our users will, and atomic support through locks i
|
+ if (ReturnType != Type::getInt64Ty(C)) return false; |
+ break; |
+ } |
+ |
+ if ((FT->getNumParams() != 5) || |
+ (FT->getParamType(0) != Type::getInt32Ty(C)) || |
+ (!FT->getParamType(1)->isPointerTy()) || |
+ (FT->getParamType(1)->getPointerElementType() != ReturnType) || |
+ (FT->getParamType(2) != ReturnType) || |
+ (FT->getParamType(3) != ReturnType) || |
+ (FT->getParamType(4) != Type::getInt32Ty(C))) |
+ return false; |
eliben
2013/06/26 16:20:57
Ditto.
JF
2013/06/26 22:23:12
Moved to a separate function, with the above too.
|
+ |
+ // Validate the operation and memory_order arguments have whitelisted values. |
+ for (Value::const_use_iterator Call(F->use_begin()), CallEnd(F->use_end()); |
eliben
2013/06/26 16:20:57
Is there a reason here to use constructor syntax f
JF
2013/06/26 22:23:12
Done.
|
+ Call != CallEnd; ++Call) { |
+ if (const CallInst *C = dyn_cast<CallInst>(*Call)) { |
+ assert(C->getNumArgOperands() == 5 && "call should have as many " |
eliben
2013/06/26 16:20:57
Should this not be caught by LLVM's normal verific
JF
2013/06/26 22:23:12
Yes, that's why I used an assert.
|
+ "arguments as the corresponding intrinsic"); |
+ const Value *Operation = C->getArgOperand(0); |
+ const Value *MemoryOrder = C->getArgOperand(4); |
+ const Constant *OperationC = dyn_cast<Constant>(Operation); |
+ const Constant *MemoryOrderC = dyn_cast<Constant>(MemoryOrder); |
+ if (!Operation || !MemoryOrder) |
eliben
2013/06/26 16:20:57
This is even worse :) You're type checking calls t
JF
2013/06/26 22:23:12
I don't agree: it's part of the intrinsic's requir
|
+ return false; |
+ const APInt &OperationI = OperationC->getUniqueInteger(); |
+ const APInt &MemoryOrderI = MemoryOrderC->getUniqueInteger(); |
+ if (OperationI.ule(NaCl::AtomicInvalid) || |
+ OperationI.uge(NaCl::AtomicNum)) |
+ return false; |
+ if (MemoryOrderI.ule(NaCl::MemoryOrderInvalid) || |
+ MemoryOrderI.uge(NaCl::MemoryOrderNum)) |
+ return false; |
+ // TODO For now only sequential consistency is allowed. |
+ if (MemoryOrderI != NaCl::MemoryOrderSequentiallyConsistent) |
+ return false; |
+ } else { |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+} |
+ |
bool PNaClABIVerifyModule::isWhitelistedIntrinsic(const Function *F, |
unsigned ID) { |
// Keep 3 categories of intrinsics for now. |
eliben
2013/06/26 16:20:57
I don't think the distinction between "always allo
JF
2013/06/26 22:23:12
If it's allowed always then it should always be al
|
// (1) Allowed always |
- // (2) Never allowed |
- // (3) "Dev" intrinsics, which may or may not be allowed. |
+ // (2) Allowed with certain restrictions. |
+ // (3) Never allowed |
+ // (4) "Dev" intrinsics, which may or may not be allowed. |
// "Dev" intrinsics are controlled by the PNaClABIAllowDevIntrinsics flag. |
// Please keep these sorted or grouped in a sensible way, within |
// each category. |
@@ -194,10 +265,6 @@ bool PNaClABIVerifyModule::isWhitelistedIntrinsic(const Function *F, |
// Disallow by default. |
default: return false; |
// (1) Always allowed. |
- case Intrinsic::bswap: return isWhitelistedBswap(F); |
- case Intrinsic::ctlz: |
- case Intrinsic::cttz: return isWhitelistedCountBits(F, 2); |
- case Intrinsic::ctpop: return isWhitelistedCountBits(F, 1); |
case Intrinsic::memcpy: |
case Intrinsic::memmove: |
case Intrinsic::memset: |
@@ -207,7 +274,17 @@ bool PNaClABIVerifyModule::isWhitelistedIntrinsic(const Function *F, |
case Intrinsic::trap: |
return true; |
- // (2) Known to be never allowed. |
+ // (2) Allowed with certain restrictions. |
+ case Intrinsic::bswap: return isWhitelistedBswap(F); |
+ case Intrinsic::ctlz: |
+ case Intrinsic::cttz: return isWhitelistedCountBits(F, 2); |
+ case Intrinsic::ctpop: return isWhitelistedCountBits(F, 1); |
+ case Intrinsic::nacl_atomic_8: |
+ case Intrinsic::nacl_atomic_16: |
+ case Intrinsic::nacl_atomic_32: |
+ case Intrinsic::nacl_atomic_64: return isWhitelistedAtomic(F, ID); |
+ |
+ // (3) Known to be never allowed. |
case Intrinsic::not_intrinsic: |
// Trampolines depend on a target-specific-sized/aligned buffer. |
case Intrinsic::adjust_trampoline: |
@@ -269,7 +346,7 @@ bool PNaClABIVerifyModule::isWhitelistedIntrinsic(const Function *F, |
case Intrinsic::flt_rounds: |
return false; |
- // (3) Dev intrinsics. |
+ // (4) Dev intrinsics. |
case Intrinsic::dbg_declare: |
case Intrinsic::dbg_value: |
return PNaClABIAllowDevIntrinsics || PNaClABIAllowDebugMetadata; |