Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: lib/Analysis/NaCl/PNaClABIVerifyFunctions.cpp

Issue 17777004: Concurrency support for PNaCl ABI (Closed) Base URL: http://git.chromium.org/native_client/pnacl-llvm.git@master
Patch Set: Clean up suggested by mseaborn. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 //===- PNaClABIVerifyFunctions.cpp - Verify PNaCl ABI rules ---------------===// 1 //===- PNaClABIVerifyFunctions.cpp - Verify PNaCl ABI rules ---------------===//
2 // 2 //
3 // The LLVM Compiler Infrastructure 3 // The LLVM Compiler Infrastructure
4 // 4 //
5 // This file is distributed under the University of Illinois Open Source 5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details. 6 // License. See LICENSE.TXT for details.
7 // 7 //
8 //===----------------------------------------------------------------------===// 8 //===----------------------------------------------------------------------===//
9 // 9 //
10 // Verify function-level PNaCl ABI requirements. 10 // Verify function-level PNaCl ABI requirements.
11 // 11 //
12 // 12 //
13 //===----------------------------------------------------------------------===// 13 //===----------------------------------------------------------------------===//
14 14
15 #include "llvm/ADT/Twine.h" 15 #include "llvm/ADT/Twine.h"
16 #include "llvm/Analysis/NaCl.h" 16 #include "llvm/Analysis/NaCl.h"
17 #include "llvm/IR/Function.h" 17 #include "llvm/IR/Function.h"
18 #include "llvm/IR/Instructions.h" 18 #include "llvm/IR/Instructions.h"
19 #include "llvm/IR/IntrinsicInst.h" 19 #include "llvm/IR/IntrinsicInst.h"
20 #include "llvm/IR/LLVMContext.h" 20 #include "llvm/IR/LLVMContext.h"
21 #include "llvm/IR/Metadata.h" 21 #include "llvm/IR/Metadata.h"
22 #include "llvm/IR/NaClIntrinsics.h"
22 #include "llvm/IR/Operator.h" 23 #include "llvm/IR/Operator.h"
23 #include "llvm/Pass.h" 24 #include "llvm/Pass.h"
24 #include "llvm/Support/raw_ostream.h" 25 #include "llvm/Support/raw_ostream.h"
25 26
26 #include "PNaClABITypeChecker.h" 27 #include "PNaClABITypeChecker.h"
27 using namespace llvm; 28 using namespace llvm;
28 29
29 namespace { 30 namespace {
30 31
31 // Checks that examine anything in the function body should be in 32 // Checks that examine anything in the function body should be in
(...skipping 14 matching lines...) Expand all
46 initializePNaClABIVerifyFunctionsPass(*PassRegistry::getPassRegistry()); 47 initializePNaClABIVerifyFunctionsPass(*PassRegistry::getPassRegistry());
47 } 48 }
48 ~PNaClABIVerifyFunctions() { 49 ~PNaClABIVerifyFunctions() {
49 if (ReporterIsOwned) 50 if (ReporterIsOwned)
50 delete Reporter; 51 delete Reporter;
51 } 52 }
52 bool runOnFunction(Function &F); 53 bool runOnFunction(Function &F);
53 virtual void print(raw_ostream &O, const Module *M) const; 54 virtual void print(raw_ostream &O, const Module *M) const;
54 private: 55 private:
55 bool IsWhitelistedMetadata(unsigned MDKind); 56 bool IsWhitelistedMetadata(unsigned MDKind);
56 const char *checkInstruction(const Instruction *Inst); 57 const char *checkInstruction(LLVMContext &C, const NaCl::AtomicIntrinsics &AI,
58 const Instruction *Inst);
57 PNaClABIErrorReporter *Reporter; 59 PNaClABIErrorReporter *Reporter;
58 bool ReporterIsOwned; 60 bool ReporterIsOwned;
59 }; 61 };
60 62
61 } // and anonymous namespace 63 } // and anonymous namespace
62 64
63 // There's no built-in way to get the name of an MDNode, so use a 65 // There's no built-in way to get the name of an MDNode, so use a
64 // string ostream to print it. 66 // string ostream to print it.
65 static std::string getMDNodeString(unsigned Kind, 67 static std::string getMDNodeString(unsigned Kind,
66 const SmallVectorImpl<StringRef> &MDNames) { 68 const SmallVectorImpl<StringRef> &MDNames) {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 if (isa<Instruction>(Val) || isa<Argument>(Val) || isa<BasicBlock>(Val)) 139 if (isa<Instruction>(Val) || isa<Argument>(Val) || isa<BasicBlock>(Val))
138 return true; 140 return true;
139 141
140 // Allow some Constants. Note that this excludes ConstantExprs. 142 // Allow some Constants. Note that this excludes ConstantExprs.
141 return PNaClABITypeChecker::isValidScalarType(Val->getType()) && 143 return PNaClABITypeChecker::isValidScalarType(Val->getType()) &&
142 (isa<ConstantInt>(Val) || 144 (isa<ConstantInt>(Val) ||
143 isa<ConstantFP>(Val) || 145 isa<ConstantFP>(Val) ||
144 isa<UndefValue>(Val)); 146 isa<UndefValue>(Val));
145 } 147 }
146 148
147 static bool isAllowedAlignment(unsigned Alignment, Type *Ty, bool IsAtomic) { 149 static bool isAllowedAlignment(unsigned Alignment, Type *Ty) {
148 if (IsAtomic) {
149 // For atomic operations, the alignment must match the size of the type.
150 if (Ty->isIntegerTy()) {
151 unsigned Bits = Ty->getIntegerBitWidth();
152 return Bits % 8 == 0 && Alignment == Bits / 8;
153 }
154 return (Ty->isDoubleTy() && Alignment == 8) ||
155 (Ty->isFloatTy() && Alignment == 4);
156 }
157 // Non-atomic integer operations must always use "align 1", since we 150 // Non-atomic integer operations must always use "align 1", since we
158 // do not want the backend to generate code with non-portable 151 // do not want the backend to generate code with non-portable
159 // undefined behaviour (such as misaligned access faults) if user 152 // undefined behaviour (such as misaligned access faults) if user
160 // code specifies "align 4" but uses a misaligned pointer. As a 153 // code specifies "align 4" but uses a misaligned pointer. As a
161 // concession to performance, we allow larger alignment values for 154 // concession to performance, we allow larger alignment values for
162 // floating point types. 155 // floating point types.
163 // 156 //
164 // To reduce the set of alignment values that need to be encoded in 157 // To reduce the set of alignment values that need to be encoded in
165 // pexes, we disallow other alignment values. We require alignments 158 // pexes, we disallow other alignment values. We require alignments
166 // to be explicit by disallowing Alignment == 0. 159 // to be explicit by disallowing Alignment == 0.
167 return Alignment == 1 || 160 return Alignment == 1 ||
168 (Ty->isDoubleTy() && Alignment == 8) || 161 (Ty->isDoubleTy() && Alignment == 8) ||
169 (Ty->isFloatTy() && Alignment == 4); 162 (Ty->isFloatTy() && Alignment == 4);
170 } 163 }
171 164
165 static bool hasAllowedAtomicRMWOperation(
166 NaCl::AtomicIntrinsics::const_iterator AI, const CallInst *Call) {
167 for (size_t P = 0; P != AI->NumParams; ++P) {
168 if (AI->ParamType[P] != NaCl::AtomicIntrinsics::RMW)
169 continue;
170
171 const Value *Operation = Call->getOperand(P);
172 if (!Operation)
173 return false;
174 const Constant *C = dyn_cast<Constant>(Operation);
175 if (!C)
176 return false;
177 const APInt &I = C->getUniqueInteger();
178 if (I.ule(NaCl::AtomicInvalid) || I.uge(NaCl::AtomicNum))
179 return false;
180 }
181 return true;
182 }
183
184 static bool hasAllowedAtomicMemoryOrder(
185 NaCl::AtomicIntrinsics::const_iterator AI, const CallInst *Call) {
186 for (size_t P = 0; P != AI->NumParams; ++P) {
187 if (AI->ParamType[P] != NaCl::AtomicIntrinsics::Mem)
188 continue;
189
190 const Value *MemoryOrder = Call->getOperand(P);
191 if (!MemoryOrder)
192 return false;
193 const Constant *C = dyn_cast<Constant>(MemoryOrder);
194 if (!C)
195 return false;
196 const APInt &I = C->getUniqueInteger();
197 if (I.ule(NaCl::MemoryOrderInvalid) || I.uge(NaCl::MemoryOrderNum))
198 return false;
199 // TODO For now only sequential consistency is allowed. When more
200 // are allowed we need to validate that the memory order is
201 // allowed on the specific atomic operation (e.g. no store
202 // acquire, and relationship between success/failure memory
203 // order on compare exchange).
204 if (I != NaCl::MemoryOrderSequentiallyConsistent)
205 return false;
206 }
207 return true;
208 }
209
172 // Check the instruction's opcode and its operands. The operands may 210 // Check the instruction's opcode and its operands. The operands may
173 // require opcode-specific checking. 211 // require opcode-specific checking.
174 // 212 //
175 // This returns an error string if the instruction is rejected, or 213 // This returns an error string if the instruction is rejected, or
176 // NULL if the instruction is allowed. 214 // NULL if the instruction is allowed.
177 const char *PNaClABIVerifyFunctions::checkInstruction(const Instruction *Inst) { 215 const char *PNaClABIVerifyFunctions::checkInstruction(LLVMContext &C,
eliben 2013/07/03 16:06:05 I think you should not carry the Context and Atomi
JF 2013/07/03 20:58:35 Done.
216 const NaCl::AtomicIntrinsics &AI, const Instruction *Inst) {
178 // If the instruction has a single pointer operand, PtrOperandIndex is 217 // If the instruction has a single pointer operand, PtrOperandIndex is
179 // set to its operand index. 218 // set to its operand index.
180 unsigned PtrOperandIndex = -1; 219 unsigned PtrOperandIndex = -1;
181 220
182 switch (Inst->getOpcode()) { 221 switch (Inst->getOpcode()) {
183 // Disallowed instructions. Default is to disallow. 222 // Disallowed instructions. Default is to disallow.
184 // We expand GetElementPtr out into arithmetic. 223 // We expand GetElementPtr out into arithmetic.
185 case Instruction::GetElementPtr: 224 case Instruction::GetElementPtr:
186 // VAArg is expanded out by ExpandVarArgs. 225 // VAArg is expanded out by ExpandVarArgs.
187 case Instruction::VAArg: 226 case Instruction::VAArg:
188 // Zero-cost C++ exception handling is not supported yet. 227 // Zero-cost C++ exception handling is not supported yet.
189 case Instruction::Invoke: 228 case Instruction::Invoke:
190 case Instruction::LandingPad: 229 case Instruction::LandingPad:
191 case Instruction::Resume: 230 case Instruction::Resume:
192 // indirectbr may interfere with streaming 231 // indirectbr may interfere with streaming
193 case Instruction::IndirectBr: 232 case Instruction::IndirectBr:
194 // No vector instructions yet 233 // No vector instructions yet
195 case Instruction::ExtractElement: 234 case Instruction::ExtractElement:
196 case Instruction::InsertElement: 235 case Instruction::InsertElement:
197 case Instruction::ShuffleVector: 236 case Instruction::ShuffleVector:
198 // ExtractValue and InsertValue operate on struct values. 237 // ExtractValue and InsertValue operate on struct values.
199 case Instruction::ExtractValue: 238 case Instruction::ExtractValue:
200 case Instruction::InsertValue: 239 case Instruction::InsertValue:
240 // Atomics should become NaCl intrinsics.
241 case Instruction::AtomicCmpXchg:
242 case Instruction::AtomicRMW:
243 case Instruction::Fence:
201 return "bad instruction opcode"; 244 return "bad instruction opcode";
202 default: 245 default:
203 return "unknown instruction opcode"; 246 return "unknown instruction opcode";
204 247
205 // Terminator instructions 248 // Terminator instructions
206 case Instruction::Ret: 249 case Instruction::Ret:
207 case Instruction::Br: 250 case Instruction::Br:
208 case Instruction::Unreachable: 251 case Instruction::Unreachable:
209 // Binary operations 252 // Binary operations
210 case Instruction::FAdd: 253 case Instruction::FAdd:
211 case Instruction::FSub: 254 case Instruction::FSub:
212 case Instruction::FMul: 255 case Instruction::FMul:
213 case Instruction::FDiv: 256 case Instruction::FDiv:
214 case Instruction::FRem: 257 case Instruction::FRem:
215 // Bitwise binary operations 258 // Bitwise binary operations
216 case Instruction::And: 259 case Instruction::And:
217 case Instruction::Or: 260 case Instruction::Or:
218 case Instruction::Xor: 261 case Instruction::Xor:
219 // Memory instructions
220 case Instruction::Fence:
221 // Conversion operations 262 // Conversion operations
222 case Instruction::Trunc: 263 case Instruction::Trunc:
223 case Instruction::ZExt: 264 case Instruction::ZExt:
224 case Instruction::SExt: 265 case Instruction::SExt:
225 case Instruction::FPTrunc: 266 case Instruction::FPTrunc:
226 case Instruction::FPExt: 267 case Instruction::FPExt:
227 case Instruction::FPToUI: 268 case Instruction::FPToUI:
228 case Instruction::FPToSI: 269 case Instruction::FPToSI:
229 case Instruction::UIToFP: 270 case Instruction::UIToFP:
230 case Instruction::SIToFP: 271 case Instruction::SIToFP:
(...skipping 18 matching lines...) Expand all
249 case Instruction::Shl: 290 case Instruction::Shl:
250 case Instruction::LShr: 291 case Instruction::LShr:
251 case Instruction::AShr: 292 case Instruction::AShr:
252 if (Inst->getOperand(0)->getType()->isIntegerTy(1)) 293 if (Inst->getOperand(0)->getType()->isIntegerTy(1))
253 return "arithmetic on i1"; 294 return "arithmetic on i1";
254 break; 295 break;
255 296
256 // Memory accesses. 297 // Memory accesses.
257 case Instruction::Load: { 298 case Instruction::Load: {
258 const LoadInst *Load = cast<LoadInst>(Inst); 299 const LoadInst *Load = cast<LoadInst>(Inst);
300 PtrOperandIndex = Load->getPointerOperandIndex();
301 if (Load->isAtomic())
302 return "atomic";
eliben 2013/07/03 16:06:05 Maybe "atomic load" / "volatile load", etc? Same b
JF 2013/07/03 20:58:35 Done. It's a bit redundant: see my corresponding c
303 if (Load->isVolatile())
304 return "volatile";
259 if (!isAllowedAlignment(Load->getAlignment(), 305 if (!isAllowedAlignment(Load->getAlignment(),
260 Load->getType(), 306 Load->getType()))
261 Load->isAtomic()))
262 return "bad alignment"; 307 return "bad alignment";
263 PtrOperandIndex = 0;
264 if (!isNormalizedPtr(Inst->getOperand(PtrOperandIndex))) 308 if (!isNormalizedPtr(Inst->getOperand(PtrOperandIndex)))
265 return "bad pointer"; 309 return "bad pointer";
266 break; 310 break;
267 } 311 }
268 case Instruction::Store: { 312 case Instruction::Store: {
269 const StoreInst *Store = cast<StoreInst>(Inst); 313 const StoreInst *Store = cast<StoreInst>(Inst);
314 PtrOperandIndex = Store->getPointerOperandIndex();
315 if (Store->isAtomic())
316 return "atomic";
317 if (Store->isVolatile())
318 return "volatile";
270 if (!isAllowedAlignment(Store->getAlignment(), 319 if (!isAllowedAlignment(Store->getAlignment(),
271 Store->getValueOperand()->getType(), 320 Store->getValueOperand()->getType()))
272 Store->isAtomic()))
273 return "bad alignment"; 321 return "bad alignment";
274 PtrOperandIndex = 1;
275 if (!isNormalizedPtr(Inst->getOperand(PtrOperandIndex))) 322 if (!isNormalizedPtr(Inst->getOperand(PtrOperandIndex)))
276 return "bad pointer"; 323 return "bad pointer";
277 break; 324 break;
278 } 325 }
279 case Instruction::AtomicCmpXchg:
280 case Instruction::AtomicRMW:
281 PtrOperandIndex = 0;
282 if (!isNormalizedPtr(Inst->getOperand(PtrOperandIndex)))
283 return "bad pointer";
284 break;
285 326
286 // Casts. 327 // Casts.
287 case Instruction::BitCast: 328 case Instruction::BitCast:
288 if (Inst->getType()->isPointerTy()) { 329 if (Inst->getType()->isPointerTy()) {
289 PtrOperandIndex = 0; 330 PtrOperandIndex = 0;
290 if (!isInherentPtr(Inst->getOperand(PtrOperandIndex))) 331 if (!isInherentPtr(Inst->getOperand(PtrOperandIndex)))
291 return "operand not InherentPtr"; 332 return "operand not InherentPtr";
292 } 333 }
293 break; 334 break;
294 case Instruction::IntToPtr: 335 case Instruction::IntToPtr:
(...skipping 30 matching lines...) Expand all
325 // metadata arguments, so handle them specially. 366 // metadata arguments, so handle them specially.
326 if (const IntrinsicInst *Call = dyn_cast<IntrinsicInst>(Inst)) { 367 if (const IntrinsicInst *Call = dyn_cast<IntrinsicInst>(Inst)) {
327 for (unsigned ArgNum = 0, E = Call->getNumArgOperands(); 368 for (unsigned ArgNum = 0, E = Call->getNumArgOperands();
328 ArgNum < E; ++ArgNum) { 369 ArgNum < E; ++ArgNum) {
329 const Value *Arg = Call->getArgOperand(ArgNum); 370 const Value *Arg = Call->getArgOperand(ArgNum);
330 if (!(isValidScalarOperand(Arg) || 371 if (!(isValidScalarOperand(Arg) ||
331 isNormalizedPtr(Arg) || 372 isNormalizedPtr(Arg) ||
332 isa<MDNode>(Arg))) 373 isa<MDNode>(Arg)))
333 return "bad intrinsic operand"; 374 return "bad intrinsic operand";
334 } 375 }
376
335 // Disallow alignments other than 1 on memcpy() etc., for the 377 // Disallow alignments other than 1 on memcpy() etc., for the
336 // same reason that we disallow them on integer loads and 378 // same reason that we disallow them on integer loads and
337 // stores. 379 // stores.
338 if (const MemIntrinsic *MemOp = dyn_cast<MemIntrinsic>(Call)) { 380 if (const MemIntrinsic *MemOp = dyn_cast<MemIntrinsic>(Call)) {
339 // Avoid the getAlignment() method here because it aborts if 381 // Avoid the getAlignment() method here because it aborts if
340 // the alignment argument is not a Constant. 382 // the alignment argument is not a Constant.
341 Value *AlignArg = MemOp->getArgOperand(3); 383 Value *AlignArg = MemOp->getArgOperand(3);
342 if (!isa<ConstantInt>(AlignArg) || 384 if (!isa<ConstantInt>(AlignArg) ||
343 cast<ConstantInt>(AlignArg)->getZExtValue() != 1) { 385 cast<ConstantInt>(AlignArg)->getZExtValue() != 1) {
344 return "bad alignment"; 386 return "bad alignment";
345 } 387 }
346 } 388 }
389
390 // Disallow NaCl atomic intrinsics which don't have valid
391 // constant NaCl::AtomicOperation and NaCl::MemoryOrder
392 // parameters.
393 switch (Call->getIntrinsicID()) {
394 default: break; // Non-atomic intrinsic.
395 case Intrinsic::nacl_atomic_load:
396 case Intrinsic::nacl_atomic_store:
397 case Intrinsic::nacl_atomic_rmw:
398 case Intrinsic::nacl_atomic_cmpxchg:
399 case Intrinsic::nacl_atomic_fence: {
400 NaCl::AtomicIntrinsics::const_iterator I =
401 AI.find(Call->getIntrinsicID(), Type::getInt32Ty(C));
402 if (!hasAllowedAtomicMemoryOrder(I, Call))
403 return "invalid memory order";
404 if (!hasAllowedAtomicRMWOperation(I, Call))
405 return "invalid atomicRMW operation";
406 } break;
407 }
408
347 // Allow the instruction and skip the later checks. 409 // Allow the instruction and skip the later checks.
348 return NULL; 410 return NULL;
349 } 411 }
350 412
351 // The callee is the last operand. 413 // The callee is the last operand.
352 PtrOperandIndex = Inst->getNumOperands() - 1; 414 PtrOperandIndex = Inst->getNumOperands() - 1;
353 if (!isNormalizedPtr(Inst->getOperand(PtrOperandIndex))) 415 if (!isNormalizedPtr(Inst->getOperand(PtrOperandIndex)))
354 return "bad function callee operand"; 416 return "bad function callee operand";
355 break; 417 break;
356 } 418 }
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
407 if (Op->isExact()) 469 if (Op->isExact())
408 return "has \"exact\" attribute"; 470 return "has \"exact\" attribute";
409 } 471 }
410 472
411 // Allow the instruction. 473 // Allow the instruction.
412 return NULL; 474 return NULL;
413 } 475 }
414 476
415 bool PNaClABIVerifyFunctions::runOnFunction(Function &F) { 477 bool PNaClABIVerifyFunctions::runOnFunction(Function &F) {
416 SmallVector<StringRef, 8> MDNames; 478 SmallVector<StringRef, 8> MDNames;
417 F.getContext().getMDKindNames(MDNames); 479 LLVMContext &C = F.getContext();
480 C.getMDKindNames(MDNames);
481
482 NaCl::AtomicIntrinsics AI(C);
418 483
419 for (Function::const_iterator FI = F.begin(), FE = F.end(); 484 for (Function::const_iterator FI = F.begin(), FE = F.end();
420 FI != FE; ++FI) { 485 FI != FE; ++FI) {
421 for (BasicBlock::const_iterator BBI = FI->begin(), BBE = FI->end(); 486 for (BasicBlock::const_iterator BBI = FI->begin(), BBE = FI->end();
422 BBI != BBE; ++BBI) { 487 BBI != BBE; ++BBI) {
423 const Instruction *Inst = BBI; 488 const Instruction *Inst = BBI;
424 // Check the instruction opcode first. This simplifies testing, 489 // Check the instruction opcode first. This simplifies testing,
425 // because some instruction opcodes must be rejected out of hand 490 // because some instruction opcodes must be rejected out of hand
426 // (regardless of the instruction's result type) and the tests 491 // (regardless of the instruction's result type) and the tests
427 // check the reason for rejection. 492 // check the reason for rejection.
428 const char *Error = checkInstruction(BBI); 493 const char *Error = checkInstruction(C, AI, BBI);
429 // Check the instruction's result type. 494 // Check the instruction's result type.
430 if (!Error && !(PNaClABITypeChecker::isValidScalarType(Inst->getType()) || 495 if (!Error && !(PNaClABITypeChecker::isValidScalarType(Inst->getType()) ||
431 isNormalizedPtr(Inst) || 496 isNormalizedPtr(Inst) ||
432 isa<AllocaInst>(Inst))) { 497 isa<AllocaInst>(Inst))) {
433 Error = "bad result type"; 498 Error = "bad result type";
434 } 499 }
435 if (Error) { 500 if (Error) {
436 Reporter->addError() << "Function " << F.getName() << 501 Reporter->addError() << "Function " << F.getName() <<
437 " disallowed: " << Error << ": " << *BBI << "\n"; 502 " disallowed: " << Error << ": " << *BBI << "\n";
438 } 503 }
(...skipping 27 matching lines...) Expand all
466 } 531 }
467 532
468 char PNaClABIVerifyFunctions::ID = 0; 533 char PNaClABIVerifyFunctions::ID = 0;
469 INITIALIZE_PASS(PNaClABIVerifyFunctions, "verify-pnaclabi-functions", 534 INITIALIZE_PASS(PNaClABIVerifyFunctions, "verify-pnaclabi-functions",
470 "Verify functions for PNaCl", false, true) 535 "Verify functions for PNaCl", false, true)
471 536
472 FunctionPass *llvm::createPNaClABIVerifyFunctionsPass( 537 FunctionPass *llvm::createPNaClABIVerifyFunctionsPass(
473 PNaClABIErrorReporter *Reporter) { 538 PNaClABIErrorReporter *Reporter) {
474 return new PNaClABIVerifyFunctions(Reporter); 539 return new PNaClABIVerifyFunctions(Reporter);
475 } 540 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698