Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-14519] is __owned not properly working anymore? #56871

Open
weissi opened this issue Apr 23, 2021 · 2 comments
Open

[SR-14519] is __owned not properly working anymore? #56871

weissi opened this issue Apr 23, 2021 · 2 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself SILOptimizer Area → compiler: SIL optimization passes

Comments

@weissi
Copy link
Member

weissi commented Apr 23, 2021

Previous ID SR-14519
Radar rdar://problem/77071198
Original Reporter @weissi
Type Bug
Environment
Apple Swift version 5.4 (swiftlang-1205.0.22.2 clang-1205.0.19.29)
Target: x86_64-apple-darwin20.4.0
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, Optimizer
Assignee None
Priority Medium

md5: acd86da8a7a814c8207858c2e7f2ae19

Issue Description:

In this Swift program

// Logger is a CoW struct
class Holder {
    var logger: Logger

    @inline(never)
    init(logger: __owned Logger) {
        var logger = logger
        logger.mutate() // <-- no CoW expected here
        self.logger = logger
    }
}

@inline(never)
func bar(holder: __owned Holder) -> Holder {
    holder.logger.mutate() // <-- no CoW expected here
    return holder
}

@inline(never)
func foo(logger: Logger) -> Holder {
    var logger = logger
    logger.mutate() // <-- CoW expected here, top-level holds onto Logger
    let holder = Holder(logger: logger) // <--
    return bar(holder: holder)
}



var a = Logger()

for _ in 0..<100000 {
    _ = foo(logger: a)
}

print(a)
// MARK: Just scaffolding

class Counter {
    var count = 0
}

struct Logger: CustomStringConvertible {
    private var counter = Counter()

    mutating func mutate() {
        if isKnownUniquelyReferenced(&self.counter) {
            self.counter.count &+= 1
        } else {
            self.counter = Counter()
        }
    }

    var description: String {
        return "Counter(\(self.counter.count))"
    }
}

I create a top level "Logger" (which is a CoW struct) and hold onto it.
The top-level then passes that Logger to foo which mutates it (should cause a CoW copy) and passes in into Holder.init (Holder is a class).

Given that foo doesn't use Logger anymore (ie it's dead) and Holder.init takes the Logger __owned I would assume that it's just moved into Holder without much ARC traffic. And I'd assume foo to just pass Logger on which should make the mutation in Holder not trigger a copy.

Sadly, this doesn't work:

$ swiftc -O -enforce-exclusivity=unchecked  test.swift && sudo dtrace -n 'pid$target::malloc_zone_malloc:entry,pid$target::malloc:entry { @calls[ustack()] = count() } :::END { printa(@calls); }' -c ./test

[...]
              // UNEXPECTED: Why do we need to CoW copy here?
              libsystem_malloc.dylib`malloc_zone_malloc
              libswiftCore.dylib`swift_slowAlloc+0x28
              libswiftCore.dylib`swift_allocObject+0x27
              test`specialized Holder.init(logger:)+0x57
              test`specialized Holder.__allocating_init(logger:)+0x2c
              test`foo(logger:)+0x6e
              test`main+0x77
              libdyld.dylib`start+0x1
              0x1
            99999

              // EXPECTED: CoW copy for Logger
              libsystem_malloc.dylib`malloc_zone_malloc
              libswiftCore.dylib`swift_slowAlloc+0x28
              libswiftCore.dylib`swift_allocObject+0x27
              test`foo(logger:)+0x50
              test`main+0x77
              libdyld.dylib`start+0x1
              0x1
            99999

              // EXPECTED: alloc for Holder itself
              libsystem_malloc.dylib`malloc_zone_malloc
              libswiftCore.dylib`swift_slowAlloc+0x28
              libswiftCore.dylib`swift_allocObject+0x27
              test`specialized Holder.__allocating_init(logger:)+0x21
              test`foo(logger:)+0x6e
              test`main+0x77
              libdyld.dylib`start+0x1
              0x1
            99999

So we have 3 sites which cause 99,999 allocations each. Only 2 are expected.

The SIL also looks interesting:

bb3:                                              // Preds: bb2 bb1
  %30 = load %2 : $*Counter                       // users: %34, %31
  %31 = struct $Logger (%30 : $Counter)           // user: %33
  // function_ref specialized Holder.__allocating_init(logger:)
  %32 = function_ref @function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Dead> of test.Holder.__allocating_init(logger: __owned test.Logger) -> test.Holder : $@convention(thin) (@guaranteed Logger) -> @owned Holder // user: %33
  %33 = apply %32(%31) : $@convention(thin) (@guaranteed Logger) -> @owned Holder // users: %37, %35

it has

test.Holder.__allocating_init(logger: __owned test.Logger) -> test.Holder : $@convention(thin) (@guaranteed Logger) -> @owned Holder

so why does __owned test.Logger become a type of $@convention(thin) (@guaranteed Logger) -> @owned Holder ?

See that it's @guaranteed and not @owned?

@weissi
Copy link
Member Author

weissi commented Apr 23, 2021

CC @gottesmm

@weissi
Copy link
Member Author

weissi commented Apr 23, 2021

@swift-ci create

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added SILOptimizer Area → compiler: SIL optimization passes and removed Optimizer labels Nov 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself SILOptimizer Area → compiler: SIL optimization passes
Projects
None yet
Development

No branches or pull requests

2 participants