]> git.saurik.com Git - apple/javascriptcore.git/blob - dfg/DFGPredictionPropagationPhase.cpp
356adb5ec3e33ca270f732561b0cf90f06b165e1
[apple/javascriptcore.git] / dfg / DFGPredictionPropagationPhase.cpp
1 /*
2 * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "DFGPredictionPropagationPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGGraph.h"
32 #include "DFGPhase.h"
33 #include "Operations.h"
34
35 namespace JSC { namespace DFG {
36
37 SpeculatedType resultOfToPrimitive(SpeculatedType type)
38 {
39 if (type & SpecObject) {
40 // Objects get turned into strings. So if the input has hints of objectness,
41 // the output will have hinsts of stringiness.
42 return mergeSpeculations(type & ~SpecObject, SpecString);
43 }
44
45 return type;
46 }
47
48 class PredictionPropagationPhase : public Phase {
49 public:
50 PredictionPropagationPhase(Graph& graph)
51 : Phase(graph, "prediction propagation")
52 {
53 }
54
55 bool run()
56 {
57 ASSERT(m_graph.m_form == ThreadedCPS);
58 ASSERT(m_graph.m_unificationState == GloballyUnified);
59
60 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
61 m_count = 0;
62 #endif
63 // 1) propagate predictions
64
65 do {
66 m_changed = false;
67
68 // Forward propagation is near-optimal for both topologically-sorted and
69 // DFS-sorted code.
70 propagateForward();
71 if (!m_changed)
72 break;
73
74 // Backward propagation reduces the likelihood that pathological code will
75 // cause slowness. Loops (especially nested ones) resemble backward flow.
76 // This pass captures two cases: (1) it detects if the forward fixpoint
77 // found a sound solution and (2) short-circuits backward flow.
78 m_changed = false;
79 propagateBackward();
80 } while (m_changed);
81
82 // 2) repropagate predictions while doing double voting.
83
84 do {
85 m_changed = false;
86 doRoundOfDoubleVoting();
87 if (!m_changed)
88 break;
89 m_changed = false;
90 propagateForward();
91 } while (m_changed);
92
93 return true;
94 }
95
96 private:
97 bool setPrediction(SpeculatedType prediction)
98 {
99 ASSERT(m_currentNode->hasResult());
100
101 // setPrediction() is used when we know that there is no way that we can change
102 // our minds about what the prediction is going to be. There is no semantic
103 // difference between setPrediction() and mergeSpeculation() other than the
104 // increased checking to validate this property.
105 ASSERT(m_currentNode->prediction() == SpecNone || m_currentNode->prediction() == prediction);
106
107 return m_currentNode->predict(prediction);
108 }
109
110 bool mergePrediction(SpeculatedType prediction)
111 {
112 ASSERT(m_currentNode->hasResult());
113
114 return m_currentNode->predict(prediction);
115 }
116
117 SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value)
118 {
119 if (!isNumberSpeculation(value))
120 return SpecDouble;
121 if (value & SpecDoubleNaN)
122 return SpecDouble;
123 return SpecDoubleReal;
124 }
125
126 SpeculatedType speculatedDoubleTypeForPredictions(SpeculatedType left, SpeculatedType right)
127 {
128 return speculatedDoubleTypeForPrediction(mergeSpeculations(left, right));
129 }
130
131 void propagate(Node* node)
132 {
133 NodeType op = node->op();
134
135 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
136 dataLog(" ", Graph::opName(op), " ", m_currentNode, ": ", NodeFlagsDump(node->flags()), " ");
137 #endif
138
139 bool changed = false;
140
141 switch (op) {
142 case JSConstant:
143 case WeakJSConstant: {
144 changed |= setPrediction(speculationFromValue(m_graph.valueOfJSConstant(node)));
145 break;
146 }
147
148 case GetLocal: {
149 VariableAccessData* variableAccessData = node->variableAccessData();
150 SpeculatedType prediction = variableAccessData->prediction();
151 if (prediction)
152 changed |= mergePrediction(prediction);
153 break;
154 }
155
156 case SetLocal: {
157 VariableAccessData* variableAccessData = node->variableAccessData();
158 changed |= variableAccessData->predict(node->child1()->prediction());
159 break;
160 }
161
162 case BitAnd:
163 case BitOr:
164 case BitXor:
165 case BitRShift:
166 case BitLShift:
167 case BitURShift:
168 case ArithIMul: {
169 changed |= setPrediction(SpecInt32);
170 break;
171 }
172
173 case ValueToInt32: {
174 changed |= setPrediction(SpecInt32);
175 break;
176 }
177
178 case ArrayPop:
179 case ArrayPush:
180 case RegExpExec:
181 case RegExpTest:
182 case GetById:
183 case GetByIdFlush:
184 case GetMyArgumentByValSafe:
185 case GetByOffset:
186 case Call:
187 case Construct:
188 case GetGlobalVar:
189 case GetScopedVar:
190 case Resolve:
191 case ResolveBase:
192 case ResolveBaseStrictPut:
193 case ResolveGlobal: {
194 changed |= setPrediction(node->getHeapPrediction());
195 break;
196 }
197
198 case StringCharCodeAt: {
199 changed |= setPrediction(SpecInt32);
200 break;
201 }
202
203 case UInt32ToNumber: {
204 if (nodeCanSpeculateInteger(node->arithNodeFlags()))
205 changed |= mergePrediction(SpecInt32);
206 else
207 changed |= mergePrediction(SpecNumber);
208 break;
209 }
210
211 case ValueAdd: {
212 SpeculatedType left = node->child1()->prediction();
213 SpeculatedType right = node->child2()->prediction();
214
215 if (left && right) {
216 if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right)) {
217 if (m_graph.addSpeculationMode(node) != DontSpeculateInteger)
218 changed |= mergePrediction(SpecInt32);
219 else
220 changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
221 } else if (!(left & SpecNumber) || !(right & SpecNumber)) {
222 // left or right is definitely something other than a number.
223 changed |= mergePrediction(SpecString);
224 } else
225 changed |= mergePrediction(SpecString | SpecInt32 | SpecDouble);
226 }
227 break;
228 }
229
230 case ArithAdd: {
231 SpeculatedType left = node->child1()->prediction();
232 SpeculatedType right = node->child2()->prediction();
233
234 if (left && right) {
235 if (m_graph.addSpeculationMode(node) != DontSpeculateInteger)
236 changed |= mergePrediction(SpecInt32);
237 else
238 changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
239 }
240 break;
241 }
242
243 case ArithSub: {
244 SpeculatedType left = node->child1()->prediction();
245 SpeculatedType right = node->child2()->prediction();
246
247 if (left && right) {
248 if (m_graph.addSpeculationMode(node) != DontSpeculateInteger)
249 changed |= mergePrediction(SpecInt32);
250 else
251 changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
252 }
253 break;
254 }
255
256 case ArithNegate:
257 if (node->child1()->prediction()) {
258 if (m_graph.negateShouldSpeculateInteger(node))
259 changed |= mergePrediction(SpecInt32);
260 else
261 changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction()));
262 }
263 break;
264
265 case ArithMin:
266 case ArithMax: {
267 SpeculatedType left = node->child1()->prediction();
268 SpeculatedType right = node->child2()->prediction();
269
270 if (left && right) {
271 if (Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node())
272 && nodeCanSpeculateInteger(node->arithNodeFlags()))
273 changed |= mergePrediction(SpecInt32);
274 else
275 changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
276 }
277 break;
278 }
279
280 case ArithMul: {
281 SpeculatedType left = node->child1()->prediction();
282 SpeculatedType right = node->child2()->prediction();
283
284 if (left && right) {
285 if (m_graph.mulShouldSpeculateInteger(node))
286 changed |= mergePrediction(SpecInt32);
287 else
288 changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
289 }
290 break;
291 }
292
293 case ArithDiv: {
294 SpeculatedType left = node->child1()->prediction();
295 SpeculatedType right = node->child2()->prediction();
296
297 if (left && right) {
298 if (Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node())
299 && nodeCanSpeculateInteger(node->arithNodeFlags()))
300 changed |= mergePrediction(SpecInt32);
301 else
302 changed |= mergePrediction(SpecDouble);
303 }
304 break;
305 }
306
307 case ArithMod: {
308 SpeculatedType left = node->child1()->prediction();
309 SpeculatedType right = node->child2()->prediction();
310
311 if (left && right) {
312 if (Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node())
313 && nodeCanSpeculateInteger(node->arithNodeFlags()))
314 changed |= mergePrediction(SpecInt32);
315 else
316 changed |= mergePrediction(SpecDouble);
317 }
318 break;
319 }
320
321 case ArithSqrt: {
322 changed |= setPrediction(SpecDouble);
323 break;
324 }
325
326 case ArithAbs: {
327 SpeculatedType child = node->child1()->prediction();
328 if (isInt32SpeculationForArithmetic(child)
329 && nodeCanSpeculateInteger(node->arithNodeFlags()))
330 changed |= mergePrediction(SpecInt32);
331 else
332 changed |= mergePrediction(speculatedDoubleTypeForPrediction(child));
333 break;
334 }
335
336 case LogicalNot:
337 case CompareLess:
338 case CompareLessEq:
339 case CompareGreater:
340 case CompareGreaterEq:
341 case CompareEq:
342 case CompareEqConstant:
343 case CompareStrictEq:
344 case CompareStrictEqConstant:
345 case InstanceOf:
346 case IsUndefined:
347 case IsBoolean:
348 case IsNumber:
349 case IsString:
350 case IsObject:
351 case IsFunction: {
352 changed |= setPrediction(SpecBoolean);
353 break;
354 }
355
356 case TypeOf: {
357 changed |= setPrediction(SpecString);
358 break;
359 }
360
361 case GetByVal: {
362 if (node->child1()->shouldSpeculateFloat32Array()
363 || node->child1()->shouldSpeculateFloat64Array())
364 changed |= mergePrediction(SpecDouble);
365 else
366 changed |= mergePrediction(node->getHeapPrediction());
367 break;
368 }
369
370 case GetMyArgumentsLengthSafe: {
371 changed |= setPrediction(SpecInt32);
372 break;
373 }
374
375 case GetScopeRegisters:
376 case GetButterfly:
377 case GetIndexedPropertyStorage:
378 case AllocatePropertyStorage:
379 case ReallocatePropertyStorage: {
380 changed |= setPrediction(SpecOther);
381 break;
382 }
383
384 case ConvertThis: {
385 SpeculatedType prediction = node->child1()->prediction();
386 if (prediction) {
387 if (prediction & ~SpecObject) {
388 prediction &= SpecObject;
389 prediction = mergeSpeculations(prediction, SpecObjectOther);
390 }
391 changed |= mergePrediction(prediction);
392 }
393 break;
394 }
395
396 case GetMyScope:
397 case SkipTopScope:
398 case SkipScope: {
399 changed |= setPrediction(SpecObjectOther);
400 break;
401 }
402
403 case GetCallee: {
404 changed |= setPrediction(SpecFunction);
405 break;
406 }
407
408 case CreateThis:
409 case NewObject: {
410 changed |= setPrediction(SpecFinalObject);
411 break;
412 }
413
414 case NewArray:
415 case NewArrayWithSize:
416 case NewArrayBuffer: {
417 changed |= setPrediction(SpecArray);
418 break;
419 }
420
421 case NewRegexp:
422 case CreateActivation: {
423 changed |= setPrediction(SpecObjectOther);
424 break;
425 }
426
427 case StringFromCharCode: {
428 changed |= setPrediction(SpecString);
429 changed |= node->child1()->mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
430 break;
431 }
432 case StringCharAt:
433 case ToString:
434 case MakeRope: {
435 changed |= setPrediction(SpecString);
436 break;
437 }
438
439 case ToPrimitive: {
440 SpeculatedType child = node->child1()->prediction();
441 if (child)
442 changed |= mergePrediction(resultOfToPrimitive(child));
443 break;
444 }
445
446 case NewStringObject: {
447 changed |= setPrediction(SpecStringObject);
448 break;
449 }
450
451 case CreateArguments: {
452 changed |= setPrediction(SpecArguments);
453 break;
454 }
455
456 case NewFunction: {
457 SpeculatedType child = node->child1()->prediction();
458 if (child & SpecEmpty)
459 changed |= mergePrediction((child & ~SpecEmpty) | SpecFunction);
460 else
461 changed |= mergePrediction(child);
462 break;
463 }
464
465 case NewFunctionNoCheck:
466 case NewFunctionExpression: {
467 changed |= setPrediction(SpecFunction);
468 break;
469 }
470
471 case PutByValAlias:
472 case GetArrayLength:
473 case Int32ToDouble:
474 case ForwardInt32ToDouble:
475 case DoubleAsInt32:
476 case GetLocalUnlinked:
477 case GetMyArgumentsLength:
478 case GetMyArgumentByVal:
479 case PhantomPutStructure:
480 case PhantomArguments:
481 case CheckArray:
482 case Arrayify:
483 case ArrayifyToStructure:
484 case MovHint:
485 case MovHintAndCheck:
486 case ZombieHint: {
487 // This node should never be visible at this stage of compilation. It is
488 // inserted by fixup(), which follows this phase.
489 CRASH();
490 break;
491 }
492
493 case Phi:
494 // Phis should not be visible here since we're iterating the all-but-Phi's
495 // part of basic blocks.
496 CRASH();
497 break;
498
499 case GetScope:
500 changed |= setPrediction(SpecObjectOther);
501 break;
502
503 case Identity:
504 changed |= mergePrediction(node->child1()->prediction());
505 break;
506
507 #ifndef NDEBUG
508 // These get ignored because they don't return anything.
509 case PutByVal:
510 case PutScopedVar:
511 case Return:
512 case Throw:
513 case PutById:
514 case PutByIdDirect:
515 case PutByOffset:
516 case SetCallee:
517 case SetMyScope:
518 case DFG::Jump:
519 case Branch:
520 case Breakpoint:
521 case CheckHasInstance:
522 case ThrowReferenceError:
523 case ForceOSRExit:
524 case SetArgument:
525 case CheckStructure:
526 case CheckExecutable:
527 case ForwardCheckStructure:
528 case StructureTransitionWatchpoint:
529 case ForwardStructureTransitionWatchpoint:
530 case CheckFunction:
531 case PutStructure:
532 case TearOffActivation:
533 case TearOffArguments:
534 case CheckArgumentsNotCreated:
535 case GlobalVarWatchpoint:
536 case GarbageValue:
537 case AllocationProfileWatchpoint:
538 case Phantom:
539 case PutGlobalVar:
540 case PutGlobalVarCheck:
541 case CheckWatchdogTimer:
542 break;
543
544 // These gets ignored because it doesn't do anything.
545 case InlineStart:
546 case Nop:
547 case CountExecution:
548 case PhantomLocal:
549 case Flush:
550 break;
551
552 case LastNodeType:
553 CRASH();
554 break;
555 #else
556 default:
557 break;
558 #endif
559 }
560
561 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
562 dataLog(SpeculationDump(node->prediction()), "\n");
563 #endif
564
565 m_changed |= changed;
566 }
567
568 void propagateForward()
569 {
570 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
571 dataLogF("Propagating predictions forward [%u]\n", ++m_count);
572 #endif
573 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
574 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
575 if (!block)
576 continue;
577 ASSERT(block->isReachable);
578 for (unsigned i = 0; i < block->size(); ++i) {
579 m_currentNode = block->at(i);
580 propagate(m_currentNode);
581 }
582 }
583 }
584
585 void propagateBackward()
586 {
587 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
588 dataLogF("Propagating predictions backward [%u]\n", ++m_count);
589 #endif
590 for (BlockIndex blockIndex = m_graph.m_blocks.size(); blockIndex--;) {
591 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
592 if (!block)
593 continue;
594 ASSERT(block->isReachable);
595 for (unsigned i = block->size(); i--;) {
596 m_currentNode = block->at(i);
597 propagate(m_currentNode);
598 }
599 }
600 }
601
602 void doDoubleVoting(Node* node)
603 {
604 switch (node->op()) {
605 case ValueAdd:
606 case ArithAdd:
607 case ArithSub: {
608 SpeculatedType left = node->child1()->prediction();
609 SpeculatedType right = node->child2()->prediction();
610
611 DoubleBallot ballot;
612
613 if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right)
614 && !m_graph.addShouldSpeculateInteger(node))
615 ballot = VoteDouble;
616 else
617 ballot = VoteValue;
618
619 m_graph.voteNode(node->child1(), ballot);
620 m_graph.voteNode(node->child2(), ballot);
621 break;
622 }
623
624 case ArithMul: {
625 SpeculatedType left = node->child1()->prediction();
626 SpeculatedType right = node->child2()->prediction();
627
628 DoubleBallot ballot;
629
630 if (isNumberSpeculation(left) && isNumberSpeculation(right)
631 && !m_graph.mulShouldSpeculateInteger(node))
632 ballot = VoteDouble;
633 else
634 ballot = VoteValue;
635
636 m_graph.voteNode(node->child1(), ballot);
637 m_graph.voteNode(node->child2(), ballot);
638 break;
639 }
640
641 case ArithMin:
642 case ArithMax:
643 case ArithMod:
644 case ArithDiv: {
645 SpeculatedType left = node->child1()->prediction();
646 SpeculatedType right = node->child2()->prediction();
647
648 DoubleBallot ballot;
649
650 if (isNumberSpeculation(left) && isNumberSpeculation(right)
651 && !(Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node()) && node->canSpeculateInteger()))
652 ballot = VoteDouble;
653 else
654 ballot = VoteValue;
655
656 m_graph.voteNode(node->child1(), ballot);
657 m_graph.voteNode(node->child2(), ballot);
658 break;
659 }
660
661 case ArithAbs:
662 DoubleBallot ballot;
663 if (!(node->child1()->shouldSpeculateIntegerForArithmetic() && node->canSpeculateInteger()))
664 ballot = VoteDouble;
665 else
666 ballot = VoteValue;
667
668 m_graph.voteNode(node->child1(), ballot);
669 break;
670
671 case ArithSqrt:
672 m_graph.voteNode(node->child1(), VoteDouble);
673 break;
674
675 case SetLocal: {
676 SpeculatedType prediction = node->child1()->prediction();
677 if (isDoubleSpeculation(prediction))
678 node->variableAccessData()->vote(VoteDouble);
679 else if (!isNumberSpeculation(prediction) || isInt32Speculation(prediction))
680 node->variableAccessData()->vote(VoteValue);
681 break;
682 }
683
684 case PutByVal:
685 case PutByValAlias: {
686 Edge child1 = m_graph.varArgChild(node, 0);
687 Edge child2 = m_graph.varArgChild(node, 1);
688 Edge child3 = m_graph.varArgChild(node, 2);
689 m_graph.voteNode(child1, VoteValue);
690 m_graph.voteNode(child2, VoteValue);
691 switch (node->arrayMode().type()) {
692 case Array::Double:
693 m_graph.voteNode(child3, VoteDouble);
694 break;
695 default:
696 m_graph.voteNode(child3, VoteValue);
697 break;
698 }
699 break;
700 }
701
702 default:
703 m_graph.voteChildren(node, VoteValue);
704 break;
705 }
706 }
707
708 void doRoundOfDoubleVoting()
709 {
710 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
711 dataLogF("Voting on double uses of locals [%u]\n", m_count);
712 #endif
713 for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i)
714 m_graph.m_variableAccessData[i].find()->clearVotes();
715 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
716 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
717 if (!block)
718 continue;
719 ASSERT(block->isReachable);
720 for (unsigned i = 0; i < block->size(); ++i) {
721 m_currentNode = block->at(i);
722 doDoubleVoting(m_currentNode);
723 }
724 }
725 for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
726 VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
727 if (!variableAccessData->isRoot())
728 continue;
729 m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat();
730 }
731 for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i)
732 m_changed |= m_graph.m_argumentPositions[i].mergeArgumentPredictionAwareness();
733 for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
734 VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
735 if (!variableAccessData->isRoot())
736 continue;
737 m_changed |= variableAccessData->makePredictionForDoubleFormat();
738 }
739 }
740
741 Node* m_currentNode;
742 bool m_changed;
743
744 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
745 unsigned m_count;
746 #endif
747 };
748
749 bool performPredictionPropagation(Graph& graph)
750 {
751 SamplingRegion samplingRegion("DFG Prediction Propagation Phase");
752 return runPhase<PredictionPropagationPhase>(graph);
753 }
754
755 } } // namespace JSC::DFG
756
757 #endif // ENABLE(DFG_JIT)
758