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

Side by Side Diff: src/IceGlobalContext.h

Issue 848193003: Subzero: Add locking to prepare for multithreaded translation. (Closed) Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master
Patch Set: Code review changes #2 Created 5 years, 11 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 //===- subzero/src/IceGlobalContext.h - Global context defs -----*- C++ -*-===// 1 //===- subzero/src/IceGlobalContext.h - Global context defs -----*- C++ -*-===//
2 // 2 //
3 // The Subzero Code Generator 3 // The Subzero Code Generator
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 // This file declares aspects of the compilation that persist across 10 // This file declares aspects of the compilation that persist across
11 // multiple functions. 11 // multiple functions.
12 // 12 //
13 //===----------------------------------------------------------------------===// 13 //===----------------------------------------------------------------------===//
14 14
15 #ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H 15 #ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H
16 #define SUBZERO_SRC_ICEGLOBALCONTEXT_H 16 #define SUBZERO_SRC_ICEGLOBALCONTEXT_H
17 17
18 #include <memory> 18 #include <memory>
19 #include <mutex>
19 20
20 #include "IceDefs.h" 21 #include "IceDefs.h"
21 #include "IceClFlags.h" 22 #include "IceClFlags.h"
22 #include "IceIntrinsics.h" 23 #include "IceIntrinsics.h"
23 #include "IceRNG.h" 24 #include "IceRNG.h"
24 #include "IceTimerTree.h" 25 #include "IceTimerTree.h"
25 #include "IceTypes.h" 26 #include "IceTypes.h"
26 27
27 namespace Ice { 28 namespace Ice {
28 29
29 class ClFlags; 30 class ClFlags;
31 class ConstantPool;
30 class FuncSigType; 32 class FuncSigType;
31 33
32 // This class collects rudimentary statistics during translation. 34 typedef std::mutex GlobalLockType;
33 class CodeStats { 35
34 CodeStats(const CodeStats &) = delete; 36 // LockedPtr is a way to provide automatically locked access to some
35 CodeStats &operator=(const CodeStats &) = default; 37 // object.
38 template <typename T> class LockedPtr {
39 LockedPtr() = delete;
40
41 // This inner class holds the actual payload. The outer class adds
42 // the unique_ptr and provides a more convenient -> operator.
43 class LockedPtrWrapper {
44 LockedPtrWrapper() = delete;
45
46 public:
47 LockedPtrWrapper(T *Value, GlobalLockType &Lock) : Value(Value), Lock(Lock) {
48 Lock.lock();
49 }
50 ~LockedPtrWrapper() { Lock.unlock(); }
51 T *operator->() const { return Value; }
52 T *operator*() const { return Value; }
53
54 private:
55 T *const Value;
56 GlobalLockType &Lock;
57 };
36 58
37 public: 59 public:
JF 2015/01/18 03:14:19 This is already public.
Jim Stichnoth 2015/01/20 16:22:54 I don't think so -- the only thing public above wa
38 CodeStats() 60 LockedPtr(T *Value, GlobalLockType &Lock) : Ptr(new LockedPtrWrapper(Value, Lo ck)) {}
JF 2015/01/18 03:14:19 The allocation here is pretty unfortunate. You wan
Jim Stichnoth 2015/01/19 21:44:05 Yeah, I think trying to use unique_ptr without an
JF 2015/01/20 01:23:04 Much better.
39 : InstructionsEmitted(0), RegistersSaved(0), FrameBytes(0), Spills(0), 61 LockedPtrWrapper &operator->() const { return *(Ptr.get()); }
40 Fills(0) {} 62 LockedPtrWrapper &operator*() const { return *(Ptr.get()); }
41 void reset() { *this = CodeStats(); }
42 void updateEmitted(uint32_t InstCount) { InstructionsEmitted += InstCount; }
43 void updateRegistersSaved(uint32_t Num) { RegistersSaved += Num; }
44 void updateFrameBytes(uint32_t Bytes) { FrameBytes += Bytes; }
45 void updateSpills() { ++Spills; }
46 void updateFills() { ++Fills; }
47 void dump(const IceString &Name, Ostream &Str);
48 63
49 private: 64 private:
50 uint32_t InstructionsEmitted; 65 std::unique_ptr<LockedPtrWrapper> Ptr;
51 uint32_t RegistersSaved;
52 uint32_t FrameBytes;
53 uint32_t Spills;
54 uint32_t Fills;
55 }; 66 };
56 67
57 // TODO: Accesses to all non-const fields of GlobalContext need to
58 // be synchronized, especially the constant pool, the allocator, and
59 // the output streams.
60 class GlobalContext { 68 class GlobalContext {
61 GlobalContext(const GlobalContext &) = delete; 69 GlobalContext(const GlobalContext &) = delete;
62 GlobalContext &operator=(const GlobalContext &) = delete; 70 GlobalContext &operator=(const GlobalContext &) = delete;
63 71
72 // CodeStats collects rudimentary statistics during translation.
73 class CodeStats {
74 CodeStats(const CodeStats &) = delete;
75 CodeStats &operator=(const CodeStats &) = default;
76
77 public:
78 CodeStats()
79 : InstructionsEmitted(0), RegistersSaved(0), FrameBytes(0), Spills(0),
80 Fills(0) {}
81 void reset() { *this = CodeStats(); }
82 void updateEmitted(uint32_t InstCount) { InstructionsEmitted += InstCount; }
83 void updateRegistersSaved(uint32_t Num) { RegistersSaved += Num; }
84 void updateFrameBytes(uint32_t Bytes) { FrameBytes += Bytes; }
85 void updateSpills() { ++Spills; }
86 void updateFills() { ++Fills; }
87 void dump(const IceString &Name, Ostream &Str);
88
89 private:
90 uint32_t InstructionsEmitted;
91 uint32_t RegistersSaved;
92 uint32_t FrameBytes;
93 uint32_t Spills;
94 uint32_t Fills;
95 };
96
97 // ThreadContext contains thread-local data. This data can be
98 // combined/reduced as needed after all threads complete.
99 class ThreadContext {
100 ThreadContext(const ThreadContext &) = delete;
101 ThreadContext &operator=(const ThreadContext &) = delete;
102
103 public:
104 ThreadContext() {}
105 CodeStats StatsFunction;
106 std::vector<TimerStack> Timers;
107 };
108
64 public: 109 public:
65 GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer, 110 GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer,
66 VerboseMask Mask, TargetArch Arch, OptLevel Opt, 111 VerboseMask Mask, TargetArch Arch, OptLevel Opt,
67 IceString TestPrefix, const ClFlags &Flags); 112 IceString TestPrefix, const ClFlags &Flags);
68 ~GlobalContext(); 113 ~GlobalContext();
69 114
70 // Returns true if any of the specified options in the verbose mask 115 // Returns true if any of the specified options in the verbose mask
71 // are set. If the argument is omitted, it checks if any verbose 116 // are set. If the argument is omitted, it checks if any verbose
72 // options at all are set. 117 // options at all are set.
73 VerboseMask getVerbose() const { return VMask; } 118 VerboseMask getVerbose() const { return VMask; }
74 bool isVerbose(VerboseMask Mask = IceV_All) const { return VMask & Mask; } 119 bool isVerbose(VerboseMask Mask = IceV_All) const { return VMask & Mask; }
75 void setVerbose(VerboseMask Mask) { VMask = Mask; } 120 void setVerbose(VerboseMask Mask) { VMask = Mask; }
76 void addVerbose(VerboseMask Mask) { VMask |= Mask; } 121 void addVerbose(VerboseMask Mask) { VMask |= Mask; }
77 void subVerbose(VerboseMask Mask) { VMask &= ~Mask; } 122 void subVerbose(VerboseMask Mask) { VMask &= ~Mask; }
78 123
79 Ostream &getStrDump() { return *StrDump; } 124 // The dump and emit streams need to be used by only one thread at a
80 Ostream &getStrEmit() { return *StrEmit; } 125 // time. This is done by exclusively reserving the streams via
126 // lockStr() and unlockStr(). The OstreamLocker class can be used
127 // to conveniently manage this.
128 //
129 // The model is that a thread grabs the stream lock, then does an
130 // arbitrary amount of work during which far-away callees may grab
131 // the stream and do something with it, and finally the thread
132 // releases the stream lock. This allows large chunks of output to
133 // be dumped or emitted without risking interleaving from multiple
134 // threads. When a worker locks the streams via lockStr(), we use
135 // IsStrLocked to verify that it wasn't already locked (i.e. no
136 // recursive lockStr() calls). When a worker grabs one of the
137 // streams via getStrDump() or getStrEmit(), we lock StrLock
138 // (recursively, if lockStr() was correctly used, hence the need for
139 // recursive_mutex) and check that IsStrLocked is set.
140 void lockStr() {
141 StrLock.lock();
142 assert(!isStrLocked());
143 IsStrLocked = true;
144 }
145 void unlockStr() {
146 assert(isStrLocked());
147 IsStrLocked = false;
148 StrLock.unlock();
149 }
150 // Test whether we are already holding StrLock, by first doing a
151 // lock() and when it (eventually) succeeds, checking that we didn't
152 // recursively lock it.
153 bool isStrLocked() {
154 StrLock.lock();
155 bool WasLocked = IsStrLocked;
156 StrLock.unlock();
157 return WasLocked;
158 }
159 Ostream &getStrDump() {
160 assert(isStrLocked());
161 return *StrDump;
162 }
163 Ostream &getStrEmit() {
164 assert(isStrLocked());
165 return *StrEmit;
166 }
81 167
82 TargetArch getTargetArch() const { return Arch; } 168 TargetArch getTargetArch() const { return Arch; }
83 OptLevel getOptLevel() const { return Opt; } 169 OptLevel getOptLevel() const { return Opt; }
84 170
85 // When emitting assembly, we allow a string to be prepended to 171 // When emitting assembly, we allow a string to be prepended to
86 // names of translated functions. This makes it easier to create an 172 // names of translated functions. This makes it easier to create an
87 // execution test against a reference translator like llc, with both 173 // execution test against a reference translator like llc, with both
88 // translators using the same bitcode as input. 174 // translators using the same bitcode as input.
89 IceString getTestPrefix() const { return TestPrefix; } 175 IceString getTestPrefix() const { return TestPrefix; }
90 IceString mangleName(const IceString &Name) const; 176 IceString mangleName(const IceString &Name) const;
(...skipping 11 matching lines...) Expand all
102 Constant *getConstantDouble(double Value); 188 Constant *getConstantDouble(double Value);
103 // Returns a symbolic constant. 189 // Returns a symbolic constant.
104 Constant *getConstantSym(RelocOffsetT Offset, const IceString &Name, 190 Constant *getConstantSym(RelocOffsetT Offset, const IceString &Name,
105 bool SuppressMangling); 191 bool SuppressMangling);
106 // Returns an undef. 192 // Returns an undef.
107 Constant *getConstantUndef(Type Ty); 193 Constant *getConstantUndef(Type Ty);
108 // Returns a zero value. 194 // Returns a zero value.
109 Constant *getConstantZero(Type Ty); 195 Constant *getConstantZero(Type Ty);
110 // getConstantPool() returns a copy of the constant pool for 196 // getConstantPool() returns a copy of the constant pool for
111 // constants of a given type. 197 // constants of a given type.
112 ConstantList getConstantPool(Type Ty) const; 198 ConstantList getConstantPool(Type Ty);
113 // Returns a new function declaration, allocated in an internal 199 // Returns a new function declaration, allocated in an internal
114 // memory pool. Ownership of the function is maintained by this 200 // memory pool. Ownership of the function is maintained by this
115 // class instance. 201 // class instance.
116 FunctionDeclaration *newFunctionDeclaration(const FuncSigType *Signature, 202 FunctionDeclaration *newFunctionDeclaration(const FuncSigType *Signature,
117 unsigned CallingConv, 203 unsigned CallingConv,
118 unsigned Linkage, bool IsProto); 204 unsigned Linkage, bool IsProto);
119 205
120 // Returns a new global variable declaration, allocated in an 206 // Returns a new global variable declaration, allocated in an
121 // internal memory pool. Ownership of the function is maintained by 207 // internal memory pool. Ownership of the function is maintained by
122 // this class instance. 208 // this class instance.
123 VariableDeclaration *newVariableDeclaration(); 209 VariableDeclaration *newVariableDeclaration();
124 210
125 const ClFlags &getFlags() const { return Flags; } 211 const ClFlags &getFlags() const { return Flags; }
126 212
127 bool isIRGenerationDisabled() const { 213 bool isIRGenerationDisabled() const {
128 return ALLOW_DISABLE_IR_GEN ? getFlags().DisableIRGeneration : false; 214 return ALLOW_DISABLE_IR_GEN ? getFlags().DisableIRGeneration : false;
129 } 215 }
130 216
131 // Allocate data of type T using the global allocator. 217 // Allocate data of type T using the global allocator.
132 template <typename T> T *allocate() { return Allocator.Allocate<T>(); } 218 template <typename T> T *allocate() {
219 return getAllocator()->Allocate<T>();
220 }
133 221
134 const Intrinsics &getIntrinsicsInfo() const { return IntrinsicsInfo; } 222 const Intrinsics &getIntrinsicsInfo() const { return IntrinsicsInfo; }
135 223
136 // TODO(wala,stichnot): Make the RNG play nicely with multithreaded 224 // TODO(wala,stichnot): Make the RNG play nicely with multithreaded
137 // translation. 225 // translation.
138 RandomNumberGenerator &getRNG() { return RNG; } 226 RandomNumberGenerator &getRNG() { return RNG; }
139 227
140 ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); } 228 ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); }
141 229
142 // Reset stats at the beginning of a function. 230 // Reset stats at the beginning of a function.
143 void resetStats() { 231 void resetStats() {
144 if (ALLOW_DUMP) 232 if (ALLOW_DUMP)
145 StatsFunction.reset(); 233 TLS->StatsFunction.reset();
146 } 234 }
147 void dumpStats(const IceString &Name, bool Final = false); 235 void dumpStats(const IceString &Name, bool Final = false);
148 void statsUpdateEmitted(uint32_t InstCount) { 236 void statsUpdateEmitted(uint32_t InstCount) {
149 if (!ALLOW_DUMP) 237 if (!ALLOW_DUMP || !getFlags().DumpStats)
150 return; 238 return;
151 StatsFunction.updateEmitted(InstCount); 239 TLS->StatsFunction.updateEmitted(InstCount);
152 StatsCumulative.updateEmitted(InstCount); 240 getStatsCumulative()->updateEmitted(InstCount);
153 } 241 }
154 void statsUpdateRegistersSaved(uint32_t Num) { 242 void statsUpdateRegistersSaved(uint32_t Num) {
155 if (!ALLOW_DUMP) 243 if (!ALLOW_DUMP || !getFlags().DumpStats)
156 return; 244 return;
157 StatsFunction.updateRegistersSaved(Num); 245 TLS->StatsFunction.updateRegistersSaved(Num);
158 StatsCumulative.updateRegistersSaved(Num); 246 getStatsCumulative()->updateRegistersSaved(Num);
159 } 247 }
160 void statsUpdateFrameBytes(uint32_t Bytes) { 248 void statsUpdateFrameBytes(uint32_t Bytes) {
161 if (!ALLOW_DUMP) 249 if (!ALLOW_DUMP || !getFlags().DumpStats)
162 return; 250 return;
163 StatsFunction.updateFrameBytes(Bytes); 251 TLS->StatsFunction.updateFrameBytes(Bytes);
164 StatsCumulative.updateFrameBytes(Bytes); 252 getStatsCumulative()->updateFrameBytes(Bytes);
165 } 253 }
166 void statsUpdateSpills() { 254 void statsUpdateSpills() {
167 if (!ALLOW_DUMP) 255 if (!ALLOW_DUMP || !getFlags().DumpStats)
168 return; 256 return;
169 StatsFunction.updateSpills(); 257 TLS->StatsFunction.updateSpills();
170 StatsCumulative.updateSpills(); 258 getStatsCumulative()->updateSpills();
171 } 259 }
172 void statsUpdateFills() { 260 void statsUpdateFills() {
173 if (!ALLOW_DUMP) 261 if (!ALLOW_DUMP || !getFlags().DumpStats)
174 return; 262 return;
175 StatsFunction.updateFills(); 263 TLS->StatsFunction.updateFills();
176 StatsCumulative.updateFills(); 264 getStatsCumulative()->updateFills();
177 } 265 }
178 266
179 // These are predefined TimerStackIdT values. 267 // These are predefined TimerStackIdT values.
180 enum TimerStackKind { 268 enum TimerStackKind {
181 TSK_Default = 0, 269 TSK_Default = 0,
182 TSK_Funcs, 270 TSK_Funcs,
183 TSK_Num 271 TSK_Num
184 }; 272 };
185 273
274 TimerStackIdT newTimerStackID(const IceString &Name);
186 TimerIdT getTimerID(TimerStackIdT StackID, const IceString &Name); 275 TimerIdT getTimerID(TimerStackIdT StackID, const IceString &Name);
187 TimerStackIdT newTimerStackID(const IceString &Name);
188 void pushTimer(TimerIdT ID, TimerStackIdT StackID = TSK_Default); 276 void pushTimer(TimerIdT ID, TimerStackIdT StackID = TSK_Default);
189 void popTimer(TimerIdT ID, TimerStackIdT StackID = TSK_Default); 277 void popTimer(TimerIdT ID, TimerStackIdT StackID = TSK_Default);
190 void resetTimer(TimerStackIdT StackID); 278 void resetTimer(TimerStackIdT StackID);
191 void setTimerName(TimerStackIdT StackID, const IceString &NewName); 279 void setTimerName(TimerStackIdT StackID, const IceString &NewName);
192 void dumpTimers(TimerStackIdT StackID = TSK_Default, 280 void dumpTimers(TimerStackIdT StackID = TSK_Default,
193 bool DumpCumulative = true); 281 bool DumpCumulative = true);
194 282
195 private: 283 private:
284 // Try to make sure the mutexes are allocated on separate cache
285 // lines, assuming the maximum cache line size is 64.
286 const static size_t MaxCacheLineSize = 64;
287 alignas(MaxCacheLineSize) GlobalLockType AllocLock;
288 alignas(MaxCacheLineSize) GlobalLockType ConstPoolLock;
289 alignas(MaxCacheLineSize) GlobalLockType StatsLock;
290 alignas(MaxCacheLineSize) GlobalLockType TimerLock;
291
292 // StrLock is a global lock on the dump and emit output streams.
293 // IsStrLocked is used to validate the locking protocol, and can
294 // only be meaningfully inspected when StrLock is held. Note that
295 // in a production build, the dump and emit streams are not used in
296 // any meaningful way, so this locking is more for
297 // development/debugging purposes.
298 typedef std::recursive_mutex StrLockType;
299 StrLockType StrLock;
300 bool IsStrLocked;
JF 2015/01/18 03:14:19 I would still drop IsStrLocked, since tsan should
Jim Stichnoth 2015/01/19 21:44:04 Right, I didn't get to that part yet. :) Here's w
JF 2015/01/20 01:23:05 Is recursive locking of the stream actually needed
Jim Stichnoth 2015/01/20 16:22:54 Currently Subzero is pretty sparing with its dump
JF 2015/01/20 17:20:21 As we discussed offline: recursive mutexes make me
Jim Stichnoth 2015/01/20 18:14:53 OK, changed to non-recursive mutex, keeping the se
301
196 Ostream *StrDump; // Stream for dumping / diagnostics 302 Ostream *StrDump; // Stream for dumping / diagnostics
197 Ostream *StrEmit; // Stream for code emission 303 Ostream *StrEmit; // Stream for code emission
198 304
199 ArenaAllocator<> Allocator; 305 ArenaAllocator<> Allocator;
200 VerboseMask VMask; 306 VerboseMask VMask;
201 std::unique_ptr<class ConstantPool> ConstPool; 307 std::unique_ptr<ConstantPool> ConstPool;
202 Intrinsics IntrinsicsInfo; 308 Intrinsics IntrinsicsInfo;
203 const TargetArch Arch; 309 const TargetArch Arch;
204 const OptLevel Opt; 310 const OptLevel Opt;
205 const IceString TestPrefix; 311 const IceString TestPrefix;
206 const ClFlags &Flags; 312 const ClFlags &Flags;
207 RandomNumberGenerator RNG; 313 RandomNumberGenerator RNG;
208 std::unique_ptr<ELFObjectWriter> ObjectWriter; 314 std::unique_ptr<ELFObjectWriter> ObjectWriter;
209 CodeStats StatsFunction;
210 CodeStats StatsCumulative; 315 CodeStats StatsCumulative;
211 std::vector<TimerStack> Timers; 316 std::vector<TimerStack> Timers;
212 std::vector<GlobalDeclaration *> GlobalDeclarations; 317 std::vector<GlobalDeclaration *> GlobalDeclarations;
213 318
319 LockedPtr<ArenaAllocator<>> getAllocator() {
320 return LockedPtr<ArenaAllocator<>>(&Allocator, AllocLock);
321 }
322 LockedPtr<ConstantPool> getConstPool() {
323 return LockedPtr<ConstantPool>(ConstPool.get(), ConstPoolLock);
324 }
325 LockedPtr<CodeStats> getStatsCumulative() {
326 return LockedPtr<CodeStats>(&StatsCumulative, StatsLock);
327 }
328 LockedPtr<std::vector<TimerStack>> getTimers() {
329 return LockedPtr<std::vector<TimerStack>>(&Timers, TimerLock);
330 }
331
332 std::vector<ThreadContext *> AllThreadContexts;
333 // Each thread has its own TLS pointer which is also held in
334 // AllThreadContexts.
335 thread_local static ThreadContext *TLS;
336
214 // Private helpers for mangleName() 337 // Private helpers for mangleName()
215 typedef llvm::SmallVector<char, 32> ManglerVector; 338 typedef llvm::SmallVector<char, 32> ManglerVector;
216 void incrementSubstitutions(ManglerVector &OldName) const; 339 void incrementSubstitutions(ManglerVector &OldName) const;
217 }; 340 };
218 341
219 // Helper class to push and pop a timer marker. The constructor 342 // Helper class to push and pop a timer marker. The constructor
220 // pushes a marker, and the destructor pops it. This is for 343 // pushes a marker, and the destructor pops it. This is for
221 // convenient timing of regions of code. 344 // convenient timing of regions of code.
222 class TimerMarker { 345 class TimerMarker {
223 TimerMarker(const TimerMarker &) = delete; 346 TimerMarker(const TimerMarker &) = delete;
(...skipping 14 matching lines...) Expand all
238 if (ALLOW_DUMP && Active) 361 if (ALLOW_DUMP && Active)
239 Ctx->popTimer(ID); 362 Ctx->popTimer(ID);
240 } 363 }
241 364
242 private: 365 private:
243 TimerIdT ID; 366 TimerIdT ID;
244 GlobalContext *const Ctx; 367 GlobalContext *const Ctx;
245 bool Active; 368 bool Active;
246 }; 369 };
247 370
371 // Helper class for locking the streams and then automatically
372 // unlocking them.
373 class OstreamLocker {
374 private:
375 OstreamLocker() = delete;
376 OstreamLocker(const OstreamLocker &) = delete;
377 OstreamLocker &operator=(const OstreamLocker &) = delete;
378
379 public:
380 explicit OstreamLocker(GlobalContext *Ctx) : Ctx(Ctx) { Ctx->lockStr(); }
381 ~OstreamLocker() { Ctx->unlockStr(); }
382
383 private:
384 GlobalContext *const Ctx;
385 };
386
248 } // end of namespace Ice 387 } // end of namespace Ice
249 388
250 #endif // SUBZERO_SRC_ICEGLOBALCONTEXT_H 389 #endif // SUBZERO_SRC_ICEGLOBALCONTEXT_H
OLDNEW
« src/IceCfg.cpp ('K') | « src/IceConverter.cpp ('k') | src/IceGlobalContext.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698