blob: 4ffd01b75a63da3ec9e0fa04a71b9145a343b426 [file] [log] [blame]
Nick Lewyckyf7a3c502010-09-07 18:14:24 +00001//===-- PTXTargetMachine.cpp - Define TargetMachine for PTX ---------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Top-level implementation for the PTX target.
11//
12//===----------------------------------------------------------------------===//
13
14#include "PTX.h"
15#include "PTXTargetMachine.h"
Eric Christopher50880d02010-09-18 18:52:28 +000016#include "llvm/PassManager.h"
Justin Holewinski40466cc2011-09-22 16:45:37 +000017#include "llvm/Analysis/Passes.h"
18#include "llvm/Analysis/Verifier.h"
19#include "llvm/Assembly/PrintModulePass.h"
20#include "llvm/ADT/OwningPtr.h"
21#include "llvm/CodeGen/AsmPrinter.h"
22#include "llvm/CodeGen/MachineFunctionAnalysis.h"
23#include "llvm/CodeGen/MachineModuleInfo.h"
24#include "llvm/CodeGen/Passes.h"
25#include "llvm/MC/MCAsmInfo.h"
26#include "llvm/MC/MCInstrInfo.h"
27#include "llvm/MC/MCStreamer.h"
28#include "llvm/MC/MCSubtargetInfo.h"
Evan Cheng3e74d6f2011-08-24 18:08:43 +000029#include "llvm/Support/TargetRegistry.h"
Che-Liang Chiouf48817c2011-03-02 07:36:48 +000030#include "llvm/Support/raw_ostream.h"
Justin Holewinski40466cc2011-09-22 16:45:37 +000031#include "llvm/Target/TargetData.h"
32#include "llvm/Target/TargetInstrInfo.h"
33#include "llvm/Target/TargetLowering.h"
34#include "llvm/Target/TargetLoweringObjectFile.h"
35#include "llvm/Target/TargetMachine.h"
36#include "llvm/Target/TargetOptions.h"
37#include "llvm/Target/TargetRegisterInfo.h"
38#include "llvm/Target/TargetSubtargetInfo.h"
39#include "llvm/Transforms/Scalar.h"
40#include "llvm/Support/Debug.h"
41#include "llvm/Support/TargetRegistry.h"
42
Nick Lewyckyf7a3c502010-09-07 18:14:24 +000043
44using namespace llvm;
45
Rafael Espindolaa484f2c2010-11-28 14:48:34 +000046namespace llvm {
47 MCStreamer *createPTXAsmStreamer(MCContext &Ctx, formatted_raw_ostream &OS,
Rafael Espindola89b93722010-12-10 07:39:47 +000048 bool isVerboseAsm, bool useLoc,
Nick Lewycky44d798d2011-10-17 23:05:28 +000049 bool useCFI, bool useDwarfDirectory,
Rafael Espindolaa484f2c2010-11-28 14:48:34 +000050 MCInstPrinter *InstPrint,
51 MCCodeEmitter *CE,
Evan Cheng78c10ee2011-07-25 23:24:55 +000052 MCAsmBackend *MAB,
Bill Wendlinge266ce62011-06-17 20:55:01 +000053 bool ShowInst);
Rafael Espindolaa484f2c2010-11-28 14:48:34 +000054}
55
Eric Christopher50880d02010-09-18 18:52:28 +000056extern "C" void LLVMInitializePTXTarget() {
Justin Holewinskie1fee482011-04-20 15:37:17 +000057
58 RegisterTargetMachine<PTX32TargetMachine> X(ThePTX32Target);
59 RegisterTargetMachine<PTX64TargetMachine> Y(ThePTX64Target);
60
Justin Holewinskie1fee482011-04-20 15:37:17 +000061 TargetRegistry::RegisterAsmStreamer(ThePTX32Target, createPTXAsmStreamer);
62 TargetRegistry::RegisterAsmStreamer(ThePTX64Target, createPTXAsmStreamer);
Nick Lewyckyf7a3c502010-09-07 18:14:24 +000063}
64
Che-Liang Chiouf48817c2011-03-02 07:36:48 +000065namespace {
Che-Liang Chiou31c488c2011-03-02 07:58:46 +000066 const char* DataLayout32 =
67 "e-p:32:32-i64:32:32-f64:32:32-v128:32:128-v64:32:64-n32:64";
68 const char* DataLayout64 =
69 "e-p:64:64-i64:32:32-f64:32:32-v128:32:128-v64:32:64-n32:64";
Che-Liang Chiouf48817c2011-03-02 07:36:48 +000070}
71
Anton Korobeynikov16c29b52011-01-10 12:39:04 +000072// DataLayout and FrameLowering are filled with dummy data
Nick Lewyckyf7a3c502010-09-07 18:14:24 +000073PTXTargetMachine::PTXTargetMachine(const Target &T,
Evan Cheng34ad6db2011-07-20 07:51:56 +000074 StringRef TT, StringRef CPU, StringRef FS,
Nick Lewycky8a8d4792011-12-02 22:16:29 +000075 const TargetOptions &Options,
Evan Cheng34ad6db2011-07-20 07:51:56 +000076 Reloc::Model RM, CodeModel::Model CM,
Evan Chengb95fc312011-11-16 08:38:26 +000077 CodeGenOpt::Level OL,
Evan Cheng34ad6db2011-07-20 07:51:56 +000078 bool is64Bit)
Nick Lewycky8a8d4792011-12-02 22:16:29 +000079 : LLVMTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL),
Justin Holewinskie1fee482011-04-20 15:37:17 +000080 DataLayout(is64Bit ? DataLayout64 : DataLayout32),
Evan Cheng276365d2011-06-30 01:53:36 +000081 Subtarget(TT, CPU, FS, is64Bit),
Anton Korobeynikov16c29b52011-01-10 12:39:04 +000082 FrameLowering(Subtarget),
Che-Liang Chiou31c488c2011-03-02 07:58:46 +000083 InstrInfo(*this),
Justin Holewinskibc97f442011-09-26 18:57:27 +000084 TSInfo(*this),
Che-Liang Chiou31c488c2011-03-02 07:58:46 +000085 TLInfo(*this) {
Eric Christopher50880d02010-09-18 18:52:28 +000086}
87
David Blaikie2d24e2a2011-12-20 02:50:00 +000088void PTX32TargetMachine::anchor() { }
89
Evan Cheng43966132011-07-19 06:37:02 +000090PTX32TargetMachine::PTX32TargetMachine(const Target &T, StringRef TT,
91 StringRef CPU, StringRef FS,
Nick Lewycky8a8d4792011-12-02 22:16:29 +000092 const TargetOptions &Options,
Evan Chengb95fc312011-11-16 08:38:26 +000093 Reloc::Model RM, CodeModel::Model CM,
94 CodeGenOpt::Level OL)
Nick Lewycky8a8d4792011-12-02 22:16:29 +000095 : PTXTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, false) {
Justin Holewinskie1fee482011-04-20 15:37:17 +000096}
97
David Blaikie2d24e2a2011-12-20 02:50:00 +000098void PTX64TargetMachine::anchor() { }
99
Evan Cheng43966132011-07-19 06:37:02 +0000100PTX64TargetMachine::PTX64TargetMachine(const Target &T, StringRef TT,
101 StringRef CPU, StringRef FS,
Nick Lewycky8a8d4792011-12-02 22:16:29 +0000102 const TargetOptions &Options,
Evan Chengb95fc312011-11-16 08:38:26 +0000103 Reloc::Model RM, CodeModel::Model CM,
104 CodeGenOpt::Level OL)
Nick Lewycky8a8d4792011-12-02 22:16:29 +0000105 : PTXTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, true) {
Justin Holewinskie1fee482011-04-20 15:37:17 +0000106}
107
Andrew Trick6939fde2012-02-06 22:51:15 +0000108namespace llvm {
Andrew Trick843ee2e2012-02-03 05:12:41 +0000109/// PTX Code Generator Pass Configuration Options.
110class PTXPassConfig : public TargetPassConfig {
111public:
Andrew Trick061efcf2012-02-04 02:56:59 +0000112 PTXPassConfig(PTXTargetMachine *TM, PassManagerBase &PM)
113 : TargetPassConfig(TM, PM) {}
Andrew Trick843ee2e2012-02-03 05:12:41 +0000114
115 PTXTargetMachine &getPTXTargetMachine() const {
116 return getTM<PTXTargetMachine>();
117 }
118
119 bool addInstSelector();
120 bool addPostRegAlloc();
121 bool addCodeGenPasses(MCContext *&OutContext);
122};
123} // namespace
124
Andrew Trick061efcf2012-02-04 02:56:59 +0000125TargetPassConfig *PTXTargetMachine::createPassConfig(PassManagerBase &PM) {
126 return new PTXPassConfig(this, PM);
Andrew Trick843ee2e2012-02-03 05:12:41 +0000127}
128
129bool PTXPassConfig::addInstSelector() {
130 PM.add(createPTXISelDag(getPTXTargetMachine(), getOptLevel()));
Che-Liang Chiouad83c1d2011-01-01 10:50:37 +0000131 return false;
132}
133
Andrew Trick843ee2e2012-02-03 05:12:41 +0000134bool PTXPassConfig::addPostRegAlloc() {
Che-Liang Chiouad83c1d2011-01-01 10:50:37 +0000135 // PTXMFInfoExtract must after register allocation!
Andrew Trick843ee2e2012-02-03 05:12:41 +0000136 //PM.add(createPTXMFInfoExtract(getPTXTargetMachine()));
Eric Christopher50880d02010-09-18 18:52:28 +0000137 return false;
Nick Lewyckyf7a3c502010-09-07 18:14:24 +0000138}
Justin Holewinski40466cc2011-09-22 16:45:37 +0000139
140bool PTXTargetMachine::addPassesToEmitFile(PassManagerBase &PM,
141 formatted_raw_ostream &Out,
142 CodeGenFileType FileType,
Justin Holewinski40466cc2011-09-22 16:45:37 +0000143 bool DisableVerify) {
144 // This is mostly based on LLVMTargetMachine::addPassesToEmitFile
145
146 // Add common CodeGen passes.
147 MCContext *Context = 0;
Andrew Trick061efcf2012-02-04 02:56:59 +0000148
149 // FIXME: soon this will be converted to use the exposed TargetPassConfig API.
Andrew Trick6939fde2012-02-06 22:51:15 +0000150 PTXPassConfig *PassConfig =
151 static_cast<PTXPassConfig*>(createPassConfig(PM));
Andrew Trick061efcf2012-02-04 02:56:59 +0000152
153 PassConfig->setDisableVerify(DisableVerify);
154
Andrew Trick6939fde2012-02-06 22:51:15 +0000155 PM.add(PassConfig);
156
Andrew Trick843ee2e2012-02-03 05:12:41 +0000157 if (PassConfig->addCodeGenPasses(Context))
Justin Holewinski40466cc2011-09-22 16:45:37 +0000158 return true;
159 assert(Context != 0 && "Failed to get MCContext");
160
161 if (hasMCSaveTempLabels())
162 Context->setAllowTemporaryLabels(false);
163
164 const MCAsmInfo &MAI = *getMCAsmInfo();
165 const MCSubtargetInfo &STI = getSubtarget<MCSubtargetInfo>();
166 OwningPtr<MCStreamer> AsmStreamer;
167
168 switch (FileType) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000169 case CGFT_AssemblyFile: {
170 MCInstPrinter *InstPrinter =
171 getTarget().createMCInstPrinter(MAI.getAssemblerDialect(), MAI, STI);
172
173 // Create a code emitter if asked to show the encoding.
174 MCCodeEmitter *MCE = 0;
175 MCAsmBackend *MAB = 0;
176
177 MCStreamer *S = getTarget().createAsmStreamer(*Context, Out,
178 true, /* verbose asm */
179 hasMCUseLoc(),
180 hasMCUseCFI(),
Nick Lewycky44d798d2011-10-17 23:05:28 +0000181 hasMCUseDwarfDirectory(),
Justin Holewinski40466cc2011-09-22 16:45:37 +0000182 InstPrinter,
183 MCE, MAB,
184 false /* show MC encoding */);
185 AsmStreamer.reset(S);
186 break;
187 }
188 case CGFT_ObjectFile: {
189 llvm_unreachable("Object file emission is not supported with PTX");
190 }
191 case CGFT_Null:
192 // The Null output is intended for use for performance analysis and testing,
193 // not real users.
194 AsmStreamer.reset(createNullStreamer(*Context));
195 break;
196 }
197
Justin Holewinski40466cc2011-09-22 16:45:37 +0000198 // Create the AsmPrinter, which takes ownership of AsmStreamer if successful.
199 FunctionPass *Printer = getTarget().createAsmPrinter(*this, *AsmStreamer);
200 if (Printer == 0)
201 return true;
202
203 // If successful, createAsmPrinter took ownership of AsmStreamer.
204 AsmStreamer.take();
205
206 PM.add(Printer);
207
208 PM.add(createGCInfoDeleter());
209 return false;
210}
211
Andrew Trick843ee2e2012-02-03 05:12:41 +0000212bool PTXPassConfig::addCodeGenPasses(MCContext *&OutContext) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000213 // Add standard LLVM codegen passes.
214 // This is derived from LLVMTargetMachine::addCommonCodeGenPasses, with some
215 // modifications for the PTX target.
216
217 // Standard LLVM-Level Passes.
218
219 // Basic AliasAnalysis support.
220 // Add TypeBasedAliasAnalysis before BasicAliasAnalysis so that
221 // BasicAliasAnalysis wins if they disagree. This is intended to help
222 // support "obvious" type-punning idioms.
223 PM.add(createTypeBasedAliasAnalysisPass());
224 PM.add(createBasicAliasAnalysisPass());
225
226 // Before running any passes, run the verifier to determine if the input
227 // coming from the front-end and/or optimizer is valid.
228 if (!DisableVerify)
229 PM.add(createVerifierPass());
230
231 // Run loop strength reduction before anything else.
Evan Chengb95fc312011-11-16 08:38:26 +0000232 if (getOptLevel() != CodeGenOpt::None) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000233 PM.add(createLoopStrengthReducePass(getTargetLowering()));
234 //PM.add(createPrintFunctionPass("\n\n*** Code after LSR ***\n", &dbgs()));
235 }
236
237 PM.add(createGCLoweringPass());
238
239 // Make sure that no unreachable blocks are instruction selected.
240 PM.add(createUnreachableBlockEliminationPass());
241
242 PM.add(createLowerInvokePass(getTargetLowering()));
243 // The lower invoke pass may create unreachable code. Remove it.
244 PM.add(createUnreachableBlockEliminationPass());
245
Evan Chengb95fc312011-11-16 08:38:26 +0000246 if (getOptLevel() != CodeGenOpt::None)
Justin Holewinski40466cc2011-09-22 16:45:37 +0000247 PM.add(createCodeGenPreparePass(getTargetLowering()));
248
249 PM.add(createStackProtectorPass(getTargetLowering()));
250
Andrew Trick843ee2e2012-02-03 05:12:41 +0000251 addPreISel();
Justin Holewinski40466cc2011-09-22 16:45:37 +0000252
253 //PM.add(createPrintFunctionPass("\n\n"
254 // "*** Final LLVM Code input to ISel ***\n",
255 // &dbgs()));
256
257 // All passes which modify the LLVM IR are now complete; run the verifier
258 // to ensure that the IR is valid.
259 if (!DisableVerify)
260 PM.add(createVerifierPass());
261
262 // Standard Lower-Level Passes.
263
264 // Install a MachineModuleInfo class, which is an immutable pass that holds
265 // all the per-module stuff we're generating, including MCContext.
Andrew Trick843ee2e2012-02-03 05:12:41 +0000266 MachineModuleInfo *MMI = new MachineModuleInfo(*TM->getMCAsmInfo(),
267 *TM->getRegisterInfo(),
Justin Holewinski05591be2011-09-22 16:45:43 +0000268 &getTargetLowering()->getObjFileLowering());
Justin Holewinski40466cc2011-09-22 16:45:37 +0000269 PM.add(MMI);
270 OutContext = &MMI->getContext(); // Return the MCContext specifically by-ref.
271
272 // Set up a MachineFunction for the rest of CodeGen to work on.
Andrew Trick843ee2e2012-02-03 05:12:41 +0000273 PM.add(new MachineFunctionAnalysis(*TM));
Justin Holewinski40466cc2011-09-22 16:45:37 +0000274
275 // Ask the target for an isel.
Andrew Trick843ee2e2012-02-03 05:12:41 +0000276 if (addInstSelector())
Justin Holewinski40466cc2011-09-22 16:45:37 +0000277 return true;
278
279 // Print the instruction selected machine code...
Andrew Trick843ee2e2012-02-03 05:12:41 +0000280 printAndVerify("After Instruction Selection");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000281
282 // Expand pseudo-instructions emitted by ISel.
283 PM.add(createExpandISelPseudosPass());
284
285 // Pre-ra tail duplication.
Evan Chengb95fc312011-11-16 08:38:26 +0000286 if (getOptLevel() != CodeGenOpt::None) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000287 PM.add(createTailDuplicatePass(true));
Andrew Trick843ee2e2012-02-03 05:12:41 +0000288 printAndVerify("After Pre-RegAlloc TailDuplicate");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000289 }
290
291 // Optimize PHIs before DCE: removing dead PHI cycles may make more
292 // instructions dead.
Evan Chengb95fc312011-11-16 08:38:26 +0000293 if (getOptLevel() != CodeGenOpt::None)
Justin Holewinski40466cc2011-09-22 16:45:37 +0000294 PM.add(createOptimizePHIsPass());
295
296 // If the target requests it, assign local variables to stack slots relative
297 // to one another and simplify frame index references where possible.
298 PM.add(createLocalStackSlotAllocationPass());
299
Evan Chengb95fc312011-11-16 08:38:26 +0000300 if (getOptLevel() != CodeGenOpt::None) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000301 // With optimization, dead code should already be eliminated. However
302 // there is one known exception: lowered code for arguments that are only
303 // used by tail calls, where the tail calls reuse the incoming stack
304 // arguments directly (see t11 in test/CodeGen/X86/sibcall.ll).
305 PM.add(createDeadMachineInstructionElimPass());
Andrew Trick843ee2e2012-02-03 05:12:41 +0000306 printAndVerify("After codegen DCE pass");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000307
308 PM.add(createMachineLICMPass());
309 PM.add(createMachineCSEPass());
310 PM.add(createMachineSinkingPass());
Andrew Trick843ee2e2012-02-03 05:12:41 +0000311 printAndVerify("After Machine LICM, CSE and Sinking passes");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000312
313 PM.add(createPeepholeOptimizerPass());
Andrew Trick843ee2e2012-02-03 05:12:41 +0000314 printAndVerify("After codegen peephole optimization pass");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000315 }
316
317 // Run pre-ra passes.
Andrew Trick843ee2e2012-02-03 05:12:41 +0000318 if (addPreRegAlloc())
319 printAndVerify("After PreRegAlloc passes");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000320
321 // Perform register allocation.
322 PM.add(createPTXRegisterAllocator());
Andrew Trick843ee2e2012-02-03 05:12:41 +0000323 printAndVerify("After Register Allocation");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000324
325 // Perform stack slot coloring and post-ra machine LICM.
Evan Chengb95fc312011-11-16 08:38:26 +0000326 if (getOptLevel() != CodeGenOpt::None) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000327 // FIXME: Re-enable coloring with register when it's capable of adding
328 // kill markers.
329 PM.add(createStackSlotColoringPass(false));
330
331 // FIXME: Post-RA LICM has asserts that fire on virtual registers.
332 // Run post-ra machine LICM to hoist reloads / remats.
333 //if (!DisablePostRAMachineLICM)
334 // PM.add(createMachineLICMPass(false));
335
Andrew Trick843ee2e2012-02-03 05:12:41 +0000336 printAndVerify("After StackSlotColoring and postra Machine LICM");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000337 }
338
339 // Run post-ra passes.
Andrew Trick843ee2e2012-02-03 05:12:41 +0000340 if (addPostRegAlloc())
341 printAndVerify("After PostRegAlloc passes");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000342
Jakob Stoklund Olesen74e2d6e2011-09-25 16:46:08 +0000343 PM.add(createExpandPostRAPseudosPass());
Andrew Trick843ee2e2012-02-03 05:12:41 +0000344 printAndVerify("After ExpandPostRAPseudos");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000345
346 // Insert prolog/epilog code. Eliminate abstract frame index references...
347 PM.add(createPrologEpilogCodeInserter());
Andrew Trick843ee2e2012-02-03 05:12:41 +0000348 printAndVerify("After PrologEpilogCodeInserter");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000349
350 // Run pre-sched2 passes.
Andrew Trick843ee2e2012-02-03 05:12:41 +0000351 if (addPreSched2())
352 printAndVerify("After PreSched2 passes");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000353
354 // Second pass scheduler.
Evan Chengb95fc312011-11-16 08:38:26 +0000355 if (getOptLevel() != CodeGenOpt::None) {
356 PM.add(createPostRAScheduler(getOptLevel()));
Andrew Trick843ee2e2012-02-03 05:12:41 +0000357 printAndVerify("After PostRAScheduler");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000358 }
359
360 // Branch folding must be run after regalloc and prolog/epilog insertion.
Evan Chengb95fc312011-11-16 08:38:26 +0000361 if (getOptLevel() != CodeGenOpt::None) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000362 PM.add(createBranchFoldingPass(getEnableTailMergeDefault()));
Andrew Trick843ee2e2012-02-03 05:12:41 +0000363 printNoVerify("After BranchFolding");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000364 }
365
366 // Tail duplication.
Evan Chengb95fc312011-11-16 08:38:26 +0000367 if (getOptLevel() != CodeGenOpt::None) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000368 PM.add(createTailDuplicatePass(false));
Andrew Trick843ee2e2012-02-03 05:12:41 +0000369 printNoVerify("After TailDuplicate");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000370 }
371
372 PM.add(createGCMachineCodeAnalysisPass());
373
374 //if (PrintGCInfo)
375 // PM.add(createGCInfoPrinter(dbgs()));
376
Evan Chengb95fc312011-11-16 08:38:26 +0000377 if (getOptLevel() != CodeGenOpt::None) {
Justin Holewinski40466cc2011-09-22 16:45:37 +0000378 PM.add(createCodePlacementOptPass());
Andrew Trick843ee2e2012-02-03 05:12:41 +0000379 printNoVerify("After CodePlacementOpt");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000380 }
381
Andrew Trick843ee2e2012-02-03 05:12:41 +0000382 if (addPreEmitPass())
383 printNoVerify("After PreEmit passes");
Justin Holewinski40466cc2011-09-22 16:45:37 +0000384
Andrew Trick843ee2e2012-02-03 05:12:41 +0000385 PM.add(createPTXMFInfoExtract(getPTXTargetMachine(), getOptLevel()));
386 PM.add(createPTXFPRoundingModePass(getPTXTargetMachine(), getOptLevel()));
Justin Holewinski6b8990d2011-09-26 16:20:25 +0000387
Justin Holewinski40466cc2011-09-22 16:45:37 +0000388 return false;
389}