[10701] implement support for conditional destruction of tuple elements.

Chris Lattner clattner at apple.com
Sun Dec 1 11:03:15 PST 2013


The model DI uses and enforces is that variables must be defined before used, and that tuple elements get independent lifetimes.  It is valid for values to be declared but never defined on some path.

-Chris

> On Dec 1, 2013, at 8:26 AM, Dave Abrahams <dabrahams at apple.com> wrote:
> 
> 
> 
> Sent from my moss-covered three-handled family gradunza
> 
>> On Dec 1, 2013, at 8:06 AM, Chris Lattner <clattner at apple.com> wrote:
>> 
>> 
>>> On Nov 30, 2013, at 10:50 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>>> 
>>> This is probably a dumb question but did we decide that class refs default-construct to null?
>> 
>> No, they don't, and they can't if class references are eventually non-nullable.
>> 
>>>  I don't remember it that way but could be mistaken.  I'd have expected DI to reject this one unless they were default constructed, in which case why should destruction be conditional?
>> 
>> Why?
> 
> Because only one of the tuple members gets initialized
> 
>> -Chris
>> 
>>> 
>>> Sent from my illudium Q-36 explosive space modulator
>>> 
>>>> On Nov 30, 2013, at 9:22 PM, clattner at apple.com wrote:
>>>> 
>>>> Revision
>>>> 10701
>>>> Author
>>>> clattner
>>>> Date
>>>> 2013-11-30 21:22:05 -0800 (Sat, 30 Nov 2013)
>>>> Log Message
>>>> 
>>>> implement support for conditional destruction of tuple elements.
>>>> 
>>>> a testcase like this:
>>>> 
>>>> func test(cond : Bool) {
>>>>   var x : (SomeClass, SomeClass)
>>>> 
>>>>   if cond {
>>>>     x.0 = getSomeClass()
>>>>   } else {
>>>>     x.1 = getSomeClass() 
>>>>   }
>>>> }
>>>> 
>>>> now ends up with an epilog to destroy "x" that looks like this:
>>>> 
>>>>   %1 = builtin_function_ref "lshr_Int2" : $@thin @callee_owned (Builtin.Int2, Builtin.Int2) -> Builtin.Int2 // user: %37
>>>>   %2 = builtin_function_ref "trunc_Int2_Int1" : $@thin @callee_owned (Builtin.Int2) -> Builtin.Int1 // users: %38, %31
>>>> ...
>>>>   %30 = load %4#1 : $*Builtin.Int2                // users: %37, %31
>>>>   %31 = apply %2(%30) : $@thin @callee_owned (Builtin.Int2) -> Builtin.Int1 // user: %32
>>>>   cond_br %31, bb4, bb5                           // id: %32
>>>> 
>>>> bb4:                                              // Preds: bb3
>>>>   %33 = tuple_element_addr %7#1 : $*(SomeClass, SomeClass), 0 // user: %34
>>>>   destroy_addr %33 : $*SomeClass                  // id: %34
>>>>   br bb5                                          // id: %35
>>>> 
>>>> bb5:                                              // Preds: bb3 bb4
>>>>   %36 = integer_literal $Builtin.Int2, 1          // user: %37
>>>>   %37 = apply %1(%30, %36) : $@thin @callee_owned (Builtin.Int2, Builtin.Int2) -> Builtin.Int2 // user: %38
>>>>   %38 = apply %2(%37) : $@thin @callee_owned (Builtin.Int2) -> Builtin.Int1 // user: %39
>>>>   cond_br %38, bb6, bb7                           // id: %39
>>>> 
>>>> bb6:                                              // Preds: bb5
>>>>   %40 = tuple_element_addr %7#1 : $*(SomeClass, SomeClass), 1 // user: %41
>>>>   destroy_addr %40 : $*SomeClass                  // id: %41
>>>>   br bb7                                          // id: %42
>>>> 
>>>> bb7:                                              // Preds: bb5 bb6
>>>>   dealloc_stack %7#0 : $*@local_storage (SomeClass, SomeClass) // id: %43
>>>> Modified Paths
>>>> 
>>>> shiny/trunk/lib/SILPasses/DefiniteInitialization.cpp
>>>> shiny/trunk/test/SILPasses/definite_initialization.shiny
>>>> Diff
>>>> 
>>>> Modified: shiny/trunk/lib/SILPasses/DefiniteInitialization.cpp (10700 => 10701)
>>>> 
>>>> 
>>>> --- shiny/trunk/lib/SILPasses/DefiniteInitialization.cpp	2013-12-01 05:10:48 UTC (rev 10700)
>>>> +++ shiny/trunk/lib/SILPasses/DefiniteInitialization.cpp	2013-12-01 05:22:05 UTC (rev 10701)
>>>> @@ -1399,6 +1399,7 @@
>>>>  
>>>>  void ElementPromotion::handleConditionalDestroys(SILValue ControlVariableAddr) {
>>>>    SILBuilder B(TheMemory);
>>>> +  SILValue ShiftRightFn, TruncateFn;
>>>>    
>>>>    // After handling any conditional initializations, check to see if we have any
>>>>    // cases where the value is only partially initialized by the time its
>>>> @@ -1425,8 +1426,9 @@
>>>>      // uninitialized, or partially initialized.  The first two cases are simple
>>>>      // to handle, whereas the partial case requires dynamic codegen based on the
>>>>      // liveness bitmask.
>>>> -    for (unsigned i = 0, e = NumTupleElements; i != e; ++i) {
>>>> -      switch (Availability.get(i)) {
>>>> +    SILValue LoadedMask;
>>>> +    for (unsigned Elt = 0, e = NumTupleElements; Elt != e; ++Elt) {
>>>> +      switch (Availability.get(Elt)) {
>>>>        case DIKind::No:
>>>>          // If an element is known to be uninitialized, then we know we can
>>>>          // completely ignore it.
>>>> @@ -1440,7 +1442,7 @@
>>>>          // If an element is known to be initialized, then we can strictly
>>>>          // destroy its value at DAI's position.
>>>>          B.setInsertionPoint(DAI);
>>>> -        SILValue EltPtr = computeTupleElementAddress(Addr, i, DAI->getLoc(), B);
>>>> +        SILValue EltPtr = computeTupleElementAddress(Addr, Elt,DAI->getLoc(),B);
>>>>          if (auto *DA = B.emitDestroyAddr(DAI->getLoc(), EltPtr))
>>>>            Releases.push_back(DA);
>>>>          continue;
>>>> @@ -1451,20 +1453,43 @@
>>>>        // This could be handled in processNonTrivialRelease some day.
>>>>        
>>>>        // Insert a load of the liveness bitmask and split the CFG into a diamond
>>>> -      // right before it.
>>>> +      // right before the destroy_addr, if we haven't already loaded it.
>>>>        B.setInsertionPoint(DAI);
>>>> -      auto *Load = B.createLoad(DAI->getLoc(), ControlVariableAddr);
>>>> +      if (!LoadedMask)
>>>> +        LoadedMask = B.createLoad(DAI->getLoc(), ControlVariableAddr);
>>>> +      SILValue CondVal = LoadedMask;
>>>>        
>>>> -      // FIXME: Shift and mask the liveness down.
>>>> -      assert(NumTupleElements == 1 && "FIXME: Broken");
>>>> +      // If this memory object has multiple tuple elements, we need to make sure
>>>> +      // to test the right one.
>>>> +      if (NumTupleElements != 1) {
>>>> +        // Shift the mask down to this element.
>>>> +        if (Elt != 0) {
>>>> +          if (!ShiftRightFn) {
>>>> +            SILBuilder FB(TheMemory->getFunction()->begin()->begin());
>>>> +            ShiftRightFn = getBinaryFunction("lshr", CondVal.getType(),
>>>> +                                             DAI->getLoc(), FB);
>>>> +          }
>>>> +          SILValue Amt = B.createIntegerLiteral(DAI->getLoc(),
>>>> +                                                CondVal.getType(), Elt);
>>>> +          SILValue Args[] = { CondVal, Amt };
>>>> +          CondVal = B.createApply(DAI->getLoc(), ShiftRightFn, Args);
>>>> +        }
>>>> +        
>>>> +        if (!TruncateFn) {
>>>> +          SILBuilder FB(TheMemory->getFunction()->begin()->begin());
>>>> +          TruncateFn = getTruncateToI1Function(CondVal.getType(),
>>>> +                                               DAI->getLoc(), FB);
>>>> +        }
>>>> +        CondVal = B.createApply(DAI->getLoc(), TruncateFn, CondVal);
>>>> +      }
>>>>        
>>>>        SILBasicBlock *CondDestroyBlock, *ContBlock;
>>>> -      InsertCFGDiamond(SILValue(Load, 0), DAI->getLoc(),
>>>> +      InsertCFGDiamond(CondVal, DAI->getLoc(),
>>>>                         B, CondDestroyBlock, nullptr, ContBlock);
>>>>        
>>>>        // Set up the conditional destroy block.
>>>>        B.setInsertionPoint(CondDestroyBlock->begin());
>>>> -      SILValue EltPtr = computeTupleElementAddress(Addr, i, DAI->getLoc(), B);
>>>> +      SILValue EltPtr = computeTupleElementAddress(Addr, Elt, DAI->getLoc(), B);
>>>>        if (auto *DA = B.emitDestroyAddr(DAI->getLoc(), EltPtr))
>>>>          Releases.push_back(DA);
>>>>      }
>>>> Modified: shiny/trunk/test/SILPasses/definite_initialization.shiny (10700 => 10701)
>>>> 
>>>> 
>>>> --- shiny/trunk/test/SILPasses/definite_initialization.shiny	2013-12-01 05:10:48 UTC (rev 10700)
>>>> +++ shiny/trunk/test/SILPasses/definite_initialization.shiny	2013-12-01 05:22:05 UTC (rev 10701)
>>>> @@ -259,6 +259,16 @@
>>>>      a.1 = 1
>>>>    }
>>>>  
>>>> +
>>>> +  func tuple_test(cond : Bool) {
>>>> +    var x : (SomeClass, SomeClass)
>>>> +
>>>> +    if cond {
>>>> +      x.0 = SomeClass()
>>>> +    } else {
>>>> +      x.1 = SomeClass()
>>>> +    }
>>>> +  }
>>>>  }
>>>>  
>>>>  // This tests cases where an store might be an init or assign based on control
>>>> @@ -276,19 +286,19 @@
>>>>    // Nontrivial type
>>>>    var sc : SomeClass
>>>>    if c {
>>>> -    sc = getSomeClass()
>>>> +    sc = SomeClass()
>>>>    }
>>>> -  sc = getSomeClass()
>>>> +  sc = SomeClass()
>>>>    
>>>>    // Tuple element types
>>>>    var tt : (SomeClass, SomeClass)
>>>>  
>>>> -  if cond {
>>>> -    tt.0 = getSomeClass()
>>>> +  if c {
>>>> +    tt.0 = SomeClass()
>>>>    } else {
>>>> -    tt.1 = getSomeClass()
>>>> +    tt.1 = SomeClass()
>>>>    }
>>>>  
>>>> -  tt.0 = getSomeClass()
>>>> +  tt.0 = SomeClass()
>>>>    tt.1 = tt.0
>>>>  }
>> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20131201/da516d00/attachment.html>


More information about the llvm-commits mailing list