@@ -1402,6 +1402,9 @@ static LegalVal legalizeInst(
1402
1402
case kIROp_GlobalParam :
1403
1403
return legalizeGlobalParam (context, cast<IRGlobalParam>(inst));
1404
1404
1405
+ case kIROp_Block :
1406
+ return LegalVal::simple (inst);
1407
+
1405
1408
default :
1406
1409
break ;
1407
1410
}
@@ -1515,27 +1518,6 @@ static void addParamType(List<IRType*>& ioParamTypes, LegalType t)
1515
1518
}
1516
1519
}
1517
1520
1518
- static void legalizeInstsInParent (
1519
- IRTypeLegalizationContext* context,
1520
- IRInst* parent)
1521
- {
1522
- IRInst* nextChild = nullptr ;
1523
- for (auto child = parent->getFirstDecorationOrChild (); child; child = nextChild)
1524
- {
1525
- nextChild = child->getNextInst ();
1526
-
1527
- if (auto block = as<IRBlock>(child))
1528
- {
1529
- legalizeInstsInParent (context, block);
1530
- }
1531
- else
1532
- {
1533
- LegalVal legalVal = legalizeInst (context, child);
1534
- registerLegalizedValue (context, child, legalVal);
1535
- }
1536
- }
1537
- }
1538
-
1539
1521
static LegalVal legalizeFunc (
1540
1522
IRTypeLegalizationContext* context,
1541
1523
IRFunc* irFunc)
@@ -1575,7 +1557,6 @@ static LegalVal legalizeFunc(
1575
1557
1576
1558
context->builder ->setDataType (irFunc, newFuncType);
1577
1559
1578
- legalizeInstsInParent (context, irFunc);
1579
1560
return LegalVal::simple (irFunc);
1580
1561
}
1581
1562
@@ -2470,19 +2451,200 @@ static LegalVal legalizeGlobalParam(
2470
2451
}
2471
2452
}
2472
2453
2454
+ struct IRTypeLegalizationPass
2455
+ {
2456
+ IRTypeLegalizationContext* context;
2457
+
2458
+ // The goal of this pass is to ensure that legalization has been
2459
+ // applied to each instruction in a module. We also want to
2460
+ // ensure that an insturction doesn't get processed until after
2461
+ // all of its operands have been processed.
2462
+ //
2463
+ // The basic idea will be to maintain a work list of instructions
2464
+ // that are able to be processed, and also a set to track which
2465
+ // instructions have ever been added to the work list.
2466
+
2467
+ List<IRInst*> workList;
2468
+ HashSet<IRInst*> addedToWorkListSet;
2469
+
2470
+ // We will add a simple query to check whether an instruciton
2471
+ // has been put on the work list before (or if it should be
2472
+ // treated *as if* it has been placed on the work list).
2473
+ //
2474
+ bool hasBeenAddedToWorkList (IRInst* inst)
2475
+ {
2476
+ // Sometimes we end up with instructions that have a null
2477
+ // operand (mostly as the type field of key instructions
2478
+ // like the module itself).
2479
+ //
2480
+ // We want to treat such null pointers like we would an
2481
+ // already-processed instruction.
2482
+ //
2483
+ if (!inst) return true ;
2484
+
2485
+ // HACK(tfoley): In most cases it is structurally invalid for our
2486
+ // IR to have a cycle where following the operands (or type) of
2487
+ // instructions can lead back to the original instruction.
2488
+ //
2489
+ // (Note that circular dependencies are still possible, but they
2490
+ // must generally be from *children* of an instruction back
2491
+ // to the instruction itself. E.g., an instruction in the body
2492
+ // of a function can directly or indirectly depend on that function.)
2493
+ //
2494
+ // The one key counterexample is with interface types, because their
2495
+ // requirements and the expected types of those requirements are
2496
+ // encoded as operands. A typical method on inteface `I` will have a type
2497
+ // that involves a `ThisType<I>` parameter for `this`, and that creates
2498
+ // a cycle back to `I`.
2499
+ //
2500
+ // In our type legalization pass we are going to manually break that
2501
+ // cycle by treating all `IRInterfaceRequirementEntry` instructions
2502
+ // as having already been processed, since there is no particular
2503
+ // need for us to handle them as part of legalization.
2504
+ //
2505
+ if (inst->op == kIROp_InterfaceRequirementEntry ) return true ;
2506
+
2507
+ return addedToWorkListSet.Contains (inst);
2508
+ }
2509
+
2510
+ // Next we define a convenience routine for adding something to the work list.
2511
+ //
2512
+ void addToWorkList (IRInst* inst)
2513
+ {
2514
+ // We want to avoid adding anything we've already added or processed.
2515
+ //
2516
+ if (addedToWorkListSet.Contains (inst))
2517
+ return ;
2518
+
2519
+ workList.add (inst);
2520
+ addedToWorkListSet.Add (inst);
2521
+ }
2522
+
2523
+ void processModule (IRModule* module)
2524
+ {
2525
+ // In order to process an entire module, we start by adding the
2526
+ // root module insturction to our work list, and then we will
2527
+ // proceed to process instructions until the work list goes dry.
2528
+ //
2529
+ addToWorkList (module->getModuleInst ());
2530
+ while ( workList.getCount () != 0 )
2531
+ {
2532
+ // The order of items in the work list is signficiant;
2533
+ // later entries could depend on earlier ones. As such, we
2534
+ // cannot just do something like the `fastRemoveAt(...)`
2535
+ // operation that could potentially lead to instructions
2536
+ // being processed in a different order than they were added.
2537
+ //
2538
+ // Instead, we will make a copy of the current work list
2539
+ // at each step, and swap in an empty work list to be added
2540
+ // to with any new instructions.
2541
+ //
2542
+ List<IRInst*> workListCopy;
2543
+ Swap (workListCopy, workList);
2544
+
2545
+ // Now we simply process each instruction on the copy of
2546
+ // the work list, knowing that `processInst` may add additional
2547
+ // instructions to the original work list.
2548
+ //
2549
+ for ( auto inst : workListCopy )
2550
+ {
2551
+ processInst (inst);
2552
+ }
2553
+ }
2554
+
2555
+ // After we are done, there might be various instructions that
2556
+ // were marked for deletion, but have not yet been cleaned up.
2557
+ //
2558
+ // We will clean up all those unnecessary instructions now.
2559
+ //
2560
+ for (auto & lv : context->replacedInstructions )
2561
+ {
2562
+ lv->removeAndDeallocate ();
2563
+ }
2564
+ }
2565
+
2566
+ void processInst (IRInst* inst)
2567
+ {
2568
+ // The main logic for legalizing an instruction is defined
2569
+ // earlier in this file.
2570
+ //
2571
+ // Our primary task here is to legalize the instruction, and then
2572
+ // register the result of legalization as the proper value
2573
+ // for that instruction.
2574
+ //
2575
+ LegalVal legalVal = legalizeInst (context, inst);
2576
+ registerLegalizedValue (context, inst, legalVal);
2577
+
2578
+ // Once the instruction has been processed, we need to consider
2579
+ // whether any other instructions might now be ready to process.
2580
+ //
2581
+ // An instruction `i` might have been blocked by `inst` if:
2582
+ //
2583
+ // * `inst` was an operand (including the type operand) of `i`, or
2584
+ // * `inst` was the parent of `i`.
2585
+ //
2586
+ // To turn those relationships around, we want to check instructions
2587
+ // `i` where:
2588
+ //
2589
+ // * `i` is a user of `inst`, or
2590
+ // * `i` is a child of `inst`.
2591
+ //
2592
+ for ( auto use = inst->firstUse ; use; use = use->nextUse )
2593
+ {
2594
+ auto user = use->getUser ();
2595
+ maybeAddToWorkList (user);
2596
+ }
2597
+ for ( auto child : inst->getDecorationsAndChildren () )
2598
+ {
2599
+ maybeAddToWorkList (child);
2600
+ }
2601
+ }
2602
+
2603
+ void maybeAddToWorkList (IRInst* inst)
2604
+ {
2605
+ // Here we have an `inst` that might be ready to go on
2606
+ // the work list, but we need to check that it would
2607
+ // be valid to put it on there.
2608
+ //
2609
+ // First, we don't want to add something if it has
2610
+ // already been added.
2611
+ //
2612
+ if (hasBeenAddedToWorkList (inst))
2613
+ return ;
2614
+
2615
+ // Next, we don't want to add something if its parent
2616
+ // hasn't been added already.
2617
+ //
2618
+ if (!hasBeenAddedToWorkList (inst->getParent ()))
2619
+ return ;
2620
+
2621
+ // Finally, we don't want to add something if its
2622
+ // type and/or operands haven't all been added.
2623
+ //
2624
+ if (!hasBeenAddedToWorkList (inst->getFullType ()))
2625
+ return ;
2626
+ Index operandCount = (Index) inst->getOperandCount ();
2627
+ for ( Index i = 0 ; i < operandCount; ++i )
2628
+ {
2629
+ auto operand = inst->getOperand (i);
2630
+ if (!hasBeenAddedToWorkList (operand))
2631
+ return ;
2632
+ }
2633
+
2634
+ // If all those checks pass, then we are ready to
2635
+ // process `inst`, and we will add it to our work list.
2636
+ //
2637
+ addToWorkList (inst);
2638
+ }
2639
+ };
2473
2640
2474
2641
static void legalizeTypes (
2475
2642
IRTypeLegalizationContext* context)
2476
2643
{
2477
- // Legalize all the top-level instructions in the module
2478
- auto module = context->module ;
2479
- legalizeInstsInParent (context, module->moduleInst );
2644
+ IRTypeLegalizationPass pass;
2645
+ pass.context = context;
2480
2646
2481
- // Clean up after any instructions we replaced along the way.
2482
- for (auto & lv : context->replacedInstructions )
2483
- {
2484
- lv->removeAndDeallocate ();
2485
- }
2647
+ pass.processModule (context->module );
2486
2648
}
2487
2649
2488
2650
// We use the same basic type legalization machinery for both simplifying
0 commit comments