OLD | NEW |
---|---|
1 //===- subzero/src/IceCfgNode.cpp - Basic block (node) implementation -----===// | 1 //===- subzero/src/IceCfgNode.cpp - Basic block (node) implementation -----===// |
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 implements the CfgNode class, including the complexities | 10 // This file implements the CfgNode class, including the complexities |
11 // of instruction insertion and in-edge calculation. | 11 // of instruction insertion and in-edge calculation. |
12 // | 12 // |
13 //===----------------------------------------------------------------------===// | 13 //===----------------------------------------------------------------------===// |
14 | 14 |
15 #include "IceCfg.h" | 15 #include "IceCfg.h" |
16 #include "IceCfgNode.h" | 16 #include "IceCfgNode.h" |
17 #include "IceInst.h" | 17 #include "IceInst.h" |
18 #include "IceLiveness.h" | |
18 #include "IceOperand.h" | 19 #include "IceOperand.h" |
19 #include "IceTargetLowering.h" | 20 #include "IceTargetLowering.h" |
20 | 21 |
21 namespace Ice { | 22 namespace Ice { |
22 | 23 |
23 CfgNode::CfgNode(Cfg *Func, SizeT LabelNumber, IceString Name) | 24 CfgNode::CfgNode(Cfg *Func, SizeT LabelNumber, IceString Name) |
24 : Func(Func), Number(LabelNumber), Name(Name), HasReturn(false) {} | 25 : Func(Func), Number(LabelNumber), Name(Name), HasReturn(false) {} |
25 | 26 |
26 // Returns the name the node was created with. If no name was given, | 27 // Returns the name the node was created with. If no name was given, |
27 // it synthesizes a (hopefully) unique name. | 28 // it synthesizes a (hopefully) unique name. |
(...skipping 14 matching lines...) Expand all Loading... | |
42 Func->setError("Phi instruction added to the middle of a block"); | 43 Func->setError("Phi instruction added to the middle of a block"); |
43 return; | 44 return; |
44 } | 45 } |
45 Phis.push_back(Phi); | 46 Phis.push_back(Phi); |
46 } else { | 47 } else { |
47 Insts.push_back(Inst); | 48 Insts.push_back(Inst); |
48 } | 49 } |
49 Inst->updateVars(this); | 50 Inst->updateVars(this); |
50 } | 51 } |
51 | 52 |
53 // Renumbers the non-deleted instructions in the node. This needs to | |
54 // be done in preparation for live range analysis. The instruction | |
55 // numbers in a block must be monotonically increasing. The range of | |
56 // instruction numbers in a block, from lowest to highest, must not | |
57 // overlap with the range of any other block. | |
58 void CfgNode::renumberInstructions() { | |
59 for (PhiList::const_iterator I = Phis.begin(), E = Phis.end(); I != E; ++I) { | |
60 (*I)->renumber(Func); | |
61 } | |
62 InstList::const_iterator I = Insts.begin(), E = Insts.end(); | |
63 while (I != E) { | |
64 Inst *Inst = *I++; | |
65 Inst->renumber(Func); | |
66 } | |
67 } | |
68 | |
52 // When a node is created, the OutEdges are immediately knows, but the | 69 // When a node is created, the OutEdges are immediately knows, but the |
53 // InEdges have to be built up incrementally. After the CFG has been | 70 // InEdges have to be built up incrementally. After the CFG has been |
54 // constructed, the computePredecessors() pass finalizes it by | 71 // constructed, the computePredecessors() pass finalizes it by |
55 // creating the InEdges list. | 72 // creating the InEdges list. |
56 void CfgNode::computePredecessors() { | 73 void CfgNode::computePredecessors() { |
57 OutEdges = (*Insts.rbegin())->getTerminatorEdges(); | 74 OutEdges = (*Insts.rbegin())->getTerminatorEdges(); |
58 for (NodeList::const_iterator I = OutEdges.begin(), E = OutEdges.end(); | 75 for (NodeList::const_iterator I = OutEdges.begin(), E = OutEdges.end(); |
59 I != E; ++I) { | 76 I != E; ++I) { |
60 CfgNode *Node = *I; | 77 CfgNode *Node = *I; |
61 Node->InEdges.push_back(this); | 78 Node->InEdges.push_back(this); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
104 // will have to be broken if the compare result is read (by the | 121 // will have to be broken if the compare result is read (by the |
105 // assignment) before it is written (by the compare). | 122 // assignment) before it is written (by the compare). |
106 InstList::iterator InsertionPoint = Insts.end(); | 123 InstList::iterator InsertionPoint = Insts.end(); |
107 // Every block must end in a terminator instruction. | 124 // Every block must end in a terminator instruction. |
108 assert(InsertionPoint != Insts.begin()); | 125 assert(InsertionPoint != Insts.begin()); |
109 --InsertionPoint; | 126 --InsertionPoint; |
110 // Confirm via assert() that InsertionPoint is a terminator | 127 // Confirm via assert() that InsertionPoint is a terminator |
111 // instruction. Calling getTerminatorEdges() on a non-terminator | 128 // instruction. Calling getTerminatorEdges() on a non-terminator |
112 // instruction will cause an llvm_unreachable(). | 129 // instruction will cause an llvm_unreachable(). |
113 assert(((*InsertionPoint)->getTerminatorEdges(), true)); | 130 assert(((*InsertionPoint)->getTerminatorEdges(), true)); |
131 if (llvm::isa<InstBr>(*InsertionPoint)) { | |
132 if (InsertionPoint != Insts.begin()) { | |
133 --InsertionPoint; | |
134 if (!llvm::isa<InstIcmp>(*InsertionPoint) && | |
135 !llvm::isa<InstFcmp>(*InsertionPoint)) { | |
136 ++InsertionPoint; | |
137 } | |
138 } | |
139 } | |
JF
2014/05/25 22:50:50
Can you explain this?
Jim Stichnoth
2014/05/29 01:39:46
Done. Also modified the logic to look only for *c
| |
114 | 140 |
115 // Consider every out-edge. | 141 // Consider every out-edge. |
116 for (NodeList::const_iterator I1 = OutEdges.begin(), E1 = OutEdges.end(); | 142 for (NodeList::const_iterator I1 = OutEdges.begin(), E1 = OutEdges.end(); |
117 I1 != E1; ++I1) { | 143 I1 != E1; ++I1) { |
118 CfgNode *Target = *I1; | 144 CfgNode *Target = *I1; |
119 // Consider every Phi instruction at the out-edge. | 145 // Consider every Phi instruction at the out-edge. |
120 for (PhiList::const_iterator I2 = Target->Phis.begin(), | 146 for (PhiList::const_iterator I2 = Target->Phis.begin(), |
121 E2 = Target->Phis.end(); | 147 E2 = Target->Phis.end(); |
122 I2 != E2; ++I2) { | 148 I2 != E2; ++I2) { |
123 Operand *Operand = (*I2)->getOperandForTarget(this); | 149 Operand *Operand = (*I2)->getOperandForTarget(this); |
(...skipping 14 matching lines...) Expand all Loading... | |
138 } | 164 } |
139 } | 165 } |
140 | 166 |
141 // Deletes the phi instructions after the loads and stores are placed. | 167 // Deletes the phi instructions after the loads and stores are placed. |
142 void CfgNode::deletePhis() { | 168 void CfgNode::deletePhis() { |
143 for (PhiList::iterator I = Phis.begin(), E = Phis.end(); I != E; ++I) { | 169 for (PhiList::iterator I = Phis.begin(), E = Phis.end(); I != E; ++I) { |
144 (*I)->setDeleted(); | 170 (*I)->setDeleted(); |
145 } | 171 } |
146 } | 172 } |
147 | 173 |
174 // Does address mode optimization. Pass each instruction to the | |
175 // TargetLowering object. If it returns a new instruction | |
176 // (representing the optimized address mode), then insert the new | |
177 // instruction and delete the old. | |
178 void CfgNode::doAddressOpt() { | |
179 TargetLowering *Target = Func->getTarget(); | |
180 LoweringContext &Context = Target->getContext(); | |
181 Context.init(this); | |
182 while (!Context.atEnd()) { | |
183 Target->doAddressOpt(); | |
184 } | |
185 } | |
186 | |
148 // Drives the target lowering. Passes the current instruction and the | 187 // Drives the target lowering. Passes the current instruction and the |
149 // next non-deleted instruction for target lowering. | 188 // next non-deleted instruction for target lowering. |
150 void CfgNode::genCode() { | 189 void CfgNode::genCode() { |
151 TargetLowering *Target = Func->getTarget(); | 190 TargetLowering *Target = Func->getTarget(); |
152 LoweringContext &Context = Target->getContext(); | 191 LoweringContext &Context = Target->getContext(); |
153 // Lower only the regular instructions. Defer the Phi instructions. | 192 // Lower only the regular instructions. Defer the Phi instructions. |
154 Context.init(this); | 193 Context.init(this); |
155 while (!Context.atEnd()) { | 194 while (!Context.atEnd()) { |
156 InstList::iterator Orig = Context.getCur(); | 195 InstList::iterator Orig = Context.getCur(); |
157 if (llvm::isa<InstRet>(*Orig)) | 196 if (llvm::isa<InstRet>(*Orig)) |
158 setHasReturn(); | 197 setHasReturn(); |
159 Target->lower(); | 198 Target->lower(); |
160 // Ensure target lowering actually moved the cursor. | 199 // Ensure target lowering actually moved the cursor. |
161 assert(Context.getCur() != Orig); | 200 assert(Context.getCur() != Orig); |
162 } | 201 } |
163 } | 202 } |
164 | 203 |
204 // Performs liveness analysis on the block. Returns true if the | |
205 // incoming liveness changed from before, false if it stayed the same. | |
206 // (If it changes, the node's predecessors need to be processed | |
207 // again.) | |
208 bool CfgNode::liveness(LivenessMode Mode, Liveness *Liveness) { | |
209 SizeT NumVars; | |
210 if (Mode == Liveness_LREndLightweight) | |
211 NumVars = Func->getNumVariables(); | |
212 else | |
213 NumVars = Liveness->getLocalSize(this); | |
214 llvm::BitVector Live(NumVars); | |
215 if (Mode != Liveness_LREndLightweight) { | |
216 // Mark the beginning and ending of each variable's live range | |
217 // with the sentinel instruction number 0. | |
218 std::vector<int> &LiveBegin = Liveness->getLiveBegin(this); | |
219 std::vector<int> &LiveEnd = Liveness->getLiveEnd(this); | |
220 LiveBegin.assign(NumVars, 0); | |
221 LiveEnd.assign(NumVars, 0); | |
222 // Initialize Live to be the union of all successors' LiveIn. | |
223 for (NodeList::const_iterator I = OutEdges.begin(), E = OutEdges.end(); | |
224 I != E; ++I) { | |
225 CfgNode *Succ = *I; | |
226 Live |= Liveness->getLiveIn(Succ); | |
227 // Mark corresponding argument of phis in successor as live. | |
228 for (PhiList::const_iterator I1 = Succ->Phis.begin(), | |
229 E1 = Succ->Phis.end(); | |
230 I1 != E1; ++I1) { | |
231 (*I1)->livenessPhiOperand(Live, this, Liveness); | |
232 } | |
233 } | |
234 Liveness->getLiveOut(this) = Live; | |
235 } | |
236 | |
237 // Process regular instructions in reverse order. | |
238 for (InstList::const_reverse_iterator I = Insts.rbegin(), E = Insts.rend(); | |
239 I != E; ++I) { | |
240 (*I)->liveness(Mode, (*I)->getNumber(), Live, Liveness, this); | |
241 } | |
242 // Process phis in forward order so that we can override the | |
243 // instruction number to be that of the earliest phi instruction in | |
244 // the block. | |
245 int32_t FirstPhiNumber = 0; // sentinel value | |
246 for (PhiList::const_iterator I = Phis.begin(), E = Phis.end(); I != E; ++I) { | |
247 if (FirstPhiNumber <= 0) | |
248 FirstPhiNumber = (*I)->getNumber(); | |
249 (*I)->liveness(Mode, FirstPhiNumber, Live, Liveness, this); | |
250 } | |
251 | |
252 // When using the sparse representation, after traversing the | |
253 // instructions in the block, the Live bitvector should only contain | |
254 // set bits for global variables upon block entry. We validate this | |
255 // by shrinking the Live vector and then testing it against the | |
256 // pre-shrunk version. (The shrinking is required, but the | |
257 // validation is not.) | |
258 if (Mode != Liveness_LREndLightweight) { | |
259 llvm::BitVector LiveOrig = Live; | |
260 Live.resize(Liveness->getGlobalSize()); | |
261 // Non-global arguments in the entry node are allowed to be live on | |
262 // entry. | |
263 bool IsEntry = (Func->getEntryNode() == this); | |
264 assert(IsEntry || Live == LiveOrig); | |
265 // The following block helps debug why the previous assertion | |
266 // failed. | |
JF
2014/05/25 22:50:50
Shouldn't the block be before the assertion?
Jim Stichnoth
2014/05/29 01:39:46
Good point. Fixed this, including replacing the a
| |
267 if (!(IsEntry || Live == LiveOrig)) { | |
268 Ostream &Str = Func->getContext()->getStrDump(); | |
269 Func->setCurrentNode(NULL); | |
270 Str << "LiveOrig-Live ="; | |
271 for (SizeT i = Live.size(); i < LiveOrig.size(); ++i) { | |
272 if (LiveOrig.test(i)) { | |
273 Str << " "; | |
274 Liveness->getVariable(i, this)->dump(Func); | |
275 } | |
276 } | |
277 Str << "\n"; | |
278 } | |
279 } | |
280 | |
281 bool Changed = false; | |
282 if (Mode != Liveness_LREndLightweight) { | |
283 llvm::BitVector &LiveIn = Liveness->getLiveIn(this); | |
284 // Add in current LiveIn | |
285 Live |= LiveIn; | |
286 // Check result, set LiveIn=Live | |
287 Changed = (Live != LiveIn); | |
288 if (Changed) | |
289 LiveIn = Live; | |
290 } | |
291 return Changed; | |
292 } | |
293 | |
294 // Now that basic liveness is complete, remove dead instructions that | |
295 // were tentatively marked as dead, and compute actual live ranges. | |
296 // It is assumed that within a single basic block, a live range begins | |
297 // at most once and ends at most once. This is certainly true for | |
298 // pure SSA form. It is also true once phis are lowered, since each | |
299 // assignment to the phi-based temporary is in a different basic | |
300 // block, and there is a single read that ends the live in the basic | |
301 // block that contained the actual phi instruction. | |
302 void CfgNode::livenessPostprocess(LivenessMode Mode, Liveness *Liveness) { | |
303 int32_t FirstInstNum = 0; | |
304 int32_t LastInstNum = 0; | |
305 // Process phis in any order. Process only Dest operands. | |
306 for (PhiList::const_iterator I = Phis.begin(), E = Phis.end(); I != E; ++I) { | |
307 InstPhi *Inst = *I; | |
308 Inst->deleteIfDead(); | |
309 if (Inst->isDeleted()) | |
310 continue; | |
311 if (FirstInstNum <= 0) | |
312 FirstInstNum = Inst->getNumber(); | |
313 assert(Inst->getNumber() > LastInstNum); | |
314 LastInstNum = Inst->getNumber(); | |
315 } | |
316 // Process instructions | |
317 for (InstList::const_iterator I = Insts.begin(), E = Insts.end(); I != E; | |
318 ++I) { | |
319 Inst *Inst = *I; | |
320 Inst->deleteIfDead(); | |
321 if (Inst->isDeleted()) | |
322 continue; | |
323 if (FirstInstNum <= 0) | |
324 FirstInstNum = Inst->getNumber(); | |
325 assert(Inst->getNumber() > LastInstNum); | |
326 LastInstNum = Inst->getNumber(); | |
327 // Create fake live ranges for a Kill instruction, but only if the | |
328 // linked instruction is still alive. | |
329 if (Mode == Liveness_RangesFull) { | |
330 if (InstFakeKill *Kill = llvm::dyn_cast<InstFakeKill>(Inst)) { | |
331 if (!Kill->getLinked()->isDeleted()) { | |
332 SizeT NumSrcs = Inst->getSrcSize(); | |
333 for (SizeT i = 0; i < NumSrcs; ++i) { | |
334 Variable *Var = llvm::cast<Variable>(Inst->getSrc(i)); | |
335 int32_t InstNumber = Inst->getNumber(); | |
336 Liveness->addLiveRange(Var, InstNumber, InstNumber, 1); | |
337 } | |
338 } | |
339 } | |
340 } | |
341 } | |
342 if (Mode != Liveness_RangesFull) | |
343 return; | |
344 | |
345 SizeT NumVars = Liveness->getLocalSize(this); | |
346 SizeT NumGlobals = Liveness->getGlobalSize(); | |
347 llvm::BitVector &LiveIn = Liveness->getLiveIn(this); | |
348 llvm::BitVector &LiveOut = Liveness->getLiveOut(this); | |
349 std::vector<int> &LiveBegin = Liveness->getLiveBegin(this); | |
350 std::vector<int> &LiveEnd = Liveness->getLiveEnd(this); | |
351 for (SizeT i = 0; i < NumVars; ++i) { | |
352 // Deal with the case where the variable is both live-in and | |
353 // live-out, but LiveEnd comes before LiveBegin. In this case, we | |
354 // need to add two segments to the live range because there is a | |
355 // hole in the middle. This would typically happen as a result of | |
356 // phi lowering in the presence of loopback edges. | |
357 bool IsGlobal = (i < NumGlobals); | |
358 if (IsGlobal && LiveIn[i] && LiveOut[i] && LiveBegin[i] > LiveEnd[i]) { | |
359 Variable *Var = Liveness->getVariable(i, this); | |
360 Liveness->addLiveRange(Var, FirstInstNum, LiveEnd[i], 1); | |
361 Liveness->addLiveRange(Var, LiveBegin[i], LastInstNum + 1, 1); | |
362 continue; | |
363 } | |
364 int32_t Begin = (IsGlobal && LiveIn[i]) ? FirstInstNum : LiveBegin[i]; | |
365 int32_t End = (IsGlobal && LiveOut[i]) ? LastInstNum + 1 : LiveEnd[i]; | |
366 if (Begin <= 0 && End <= 0) | |
367 continue; | |
368 if (Begin <= FirstInstNum) | |
369 Begin = FirstInstNum; | |
370 if (End <= 0) | |
371 End = LastInstNum + 1; | |
372 Variable *Var = Liveness->getVariable(i, this); | |
373 Liveness->addLiveRange(Var, Begin, End, 1); | |
374 } | |
375 } | |
376 | |
165 // ======================== Dump routines ======================== // | 377 // ======================== Dump routines ======================== // |
166 | 378 |
167 void CfgNode::emit(Cfg *Func) const { | 379 void CfgNode::emit(Cfg *Func) const { |
168 Func->setCurrentNode(this); | 380 Func->setCurrentNode(this); |
169 Ostream &Str = Func->getContext()->getStrEmit(); | 381 Ostream &Str = Func->getContext()->getStrEmit(); |
170 if (Func->getEntryNode() == this) { | 382 if (Func->getEntryNode() == this) { |
171 Str << Func->getContext()->mangleName(Func->getFunctionName()) << ":\n"; | 383 Str << Func->getContext()->mangleName(Func->getFunctionName()) << ":\n"; |
172 } | 384 } |
173 Str << getAsmName() << ":\n"; | 385 Str << getAsmName() << ":\n"; |
174 for (PhiList::const_iterator I = Phis.begin(), E = Phis.end(); I != E; ++I) { | 386 for (PhiList::const_iterator I = Phis.begin(), E = Phis.end(); I != E; ++I) { |
(...skipping 12 matching lines...) Expand all Loading... | |
187 // suppress them. | 399 // suppress them. |
188 if (Inst->isRedundantAssign()) | 400 if (Inst->isRedundantAssign()) |
189 continue; | 401 continue; |
190 (*I)->emit(Func); | 402 (*I)->emit(Func); |
191 } | 403 } |
192 } | 404 } |
193 | 405 |
194 void CfgNode::dump(Cfg *Func) const { | 406 void CfgNode::dump(Cfg *Func) const { |
195 Func->setCurrentNode(this); | 407 Func->setCurrentNode(this); |
196 Ostream &Str = Func->getContext()->getStrDump(); | 408 Ostream &Str = Func->getContext()->getStrDump(); |
409 Liveness *Liveness = Func->getLiveness(); | |
197 if (Func->getContext()->isVerbose(IceV_Instructions)) { | 410 if (Func->getContext()->isVerbose(IceV_Instructions)) { |
198 Str << getName() << ":\n"; | 411 Str << getName() << ":\n"; |
199 } | 412 } |
200 // Dump list of predecessor nodes. | 413 // Dump list of predecessor nodes. |
201 if (Func->getContext()->isVerbose(IceV_Preds) && !InEdges.empty()) { | 414 if (Func->getContext()->isVerbose(IceV_Preds) && !InEdges.empty()) { |
202 Str << " // preds = "; | 415 Str << " // preds = "; |
203 for (NodeList::const_iterator I = InEdges.begin(), E = InEdges.end(); | 416 for (NodeList::const_iterator I = InEdges.begin(), E = InEdges.end(); |
204 I != E; ++I) { | 417 I != E; ++I) { |
205 if (I != InEdges.begin()) | 418 if (I != InEdges.begin()) |
206 Str << ", "; | 419 Str << ", "; |
207 Str << "%" << (*I)->getName(); | 420 Str << "%" << (*I)->getName(); |
208 } | 421 } |
209 Str << "\n"; | 422 Str << "\n"; |
210 } | 423 } |
424 // Dump the live-in variables. | |
425 llvm::BitVector LiveIn; | |
426 if (Liveness) | |
427 LiveIn = Liveness->getLiveIn(this); | |
428 if (Func->getContext()->isVerbose(IceV_Liveness) && !LiveIn.empty()) { | |
429 Str << " // LiveIn:"; | |
430 for (SizeT i = 0; i < LiveIn.size(); ++i) { | |
431 if (LiveIn[i]) { | |
432 Str << " %" << Liveness->getVariable(i, this)->getName(); | |
433 } | |
434 } | |
435 Str << "\n"; | |
436 } | |
211 // Dump each instruction. | 437 // Dump each instruction. |
212 if (Func->getContext()->isVerbose(IceV_Instructions)) { | 438 if (Func->getContext()->isVerbose(IceV_Instructions)) { |
213 for (PhiList::const_iterator I = Phis.begin(), E = Phis.end(); I != E; | 439 for (PhiList::const_iterator I = Phis.begin(), E = Phis.end(); I != E; |
214 ++I) { | 440 ++I) { |
215 const Inst *Inst = *I; | 441 const Inst *Inst = *I; |
216 Inst->dumpDecorated(Func); | 442 Inst->dumpDecorated(Func); |
217 } | 443 } |
218 InstList::const_iterator I = Insts.begin(), E = Insts.end(); | 444 InstList::const_iterator I = Insts.begin(), E = Insts.end(); |
219 while (I != E) { | 445 while (I != E) { |
220 Inst *Inst = *I++; | 446 Inst *Inst = *I++; |
221 Inst->dumpDecorated(Func); | 447 Inst->dumpDecorated(Func); |
222 } | 448 } |
223 } | 449 } |
450 // Dump the live-out variables. | |
451 llvm::BitVector LiveOut; | |
452 if (Liveness) | |
453 LiveOut = Liveness->getLiveOut(this); | |
454 if (Func->getContext()->isVerbose(IceV_Liveness) && !LiveOut.empty()) { | |
455 Str << " // LiveOut:"; | |
456 for (SizeT i = 0; i < LiveOut.size(); ++i) { | |
457 if (LiveOut[i]) { | |
458 Str << " %" << Liveness->getVariable(i, this)->getName(); | |
459 } | |
460 } | |
461 Str << "\n"; | |
462 } | |
224 // Dump list of successor nodes. | 463 // Dump list of successor nodes. |
225 if (Func->getContext()->isVerbose(IceV_Succs)) { | 464 if (Func->getContext()->isVerbose(IceV_Succs)) { |
226 Str << " // succs = "; | 465 Str << " // succs = "; |
227 for (NodeList::const_iterator I = OutEdges.begin(), E = OutEdges.end(); | 466 for (NodeList::const_iterator I = OutEdges.begin(), E = OutEdges.end(); |
228 I != E; ++I) { | 467 I != E; ++I) { |
229 if (I != OutEdges.begin()) | 468 if (I != OutEdges.begin()) |
230 Str << ", "; | 469 Str << ", "; |
231 Str << "%" << (*I)->getName(); | 470 Str << "%" << (*I)->getName(); |
232 } | 471 } |
233 Str << "\n"; | 472 Str << "\n"; |
234 } | 473 } |
235 } | 474 } |
236 | 475 |
237 } // end of namespace Ice | 476 } // end of namespace Ice |
OLD | NEW |