The @effects attribute?

I'd like to know more about @effects attribute.

Apart from the document linked to above, I haven't been able to find anything.

As it's available in Xcode 9.4 and doesn't have an underscore I assume that it's supposed to be usable not only in the implementation of the standard library?

Where should I go to find more info, implementation status etc?


I noticed that while the following (command line prg) compiles with Xcode 9.4 (default toolchain), it always prints 0 instead of the expected 123:

@effects(readnone)
func f<T>(_ a: T) -> T { return a }

let x = f(123)
print(x) // Prints 0, not 123

(Note that I'm only talking about release builds / -O, with a debug build it will print 123.)


And, if I stick the top level statements in a func, it prints "random" values:

@effects(readnone)
func f<T>(_ a: T) -> T { return a }

func test() {
    let x = f(123)
    print(x) // Prints "random" (uninitialized?) values.
}
test()

Also, if I use a recent dev snapshot (2018-06-03), the first example will work the same (printing 0), but the second example will crash the compiler.

(I'm still talking only about release builds, as I guess the attribute has no effect in debug builds? (It won't crash the compiler when doing a debug build)).

Here's what the compiler crash looks like
› /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2018-06-03-a.xctoolchain/usr/bin/swiftc --version
Apple Swift version 4.2-dev (LLVM 58850e66ae, Clang b58a7ad218, Swift feb385736b)
Target: x86_64-apple-darwin17.5.0
› /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2018-06-03-a.xctoolchain/usr/bin/swiftc -O test.swift
Assertion failed: (ASI->getElementType().isVoid() && "Expected initialization of non-void type!"), function removeSingleBlockAllocation, file /Users/buildnode/jenkins/workspace/oss-swift-package-osx/swift/lib/SILOptimizer/Transforms/SILMem2Reg.cpp, line 470.
0  swift                    0x000000010f193b08 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40
1  swift                    0x000000010f192d77 llvm::sys::RunSignalHandlers() + 39
2  swift                    0x000000010f194182 SignalHandler(int) + 258
3  libsystem_platform.dylib 0x00007fff79195f5a _sigtramp + 26
4  libsystem_platform.dylib 0x00007fb4340f3d40 _sigtramp + 3136675328
5  libsystem_c.dylib        0x00007fff78f331ae abort + 127
6  libsystem_c.dylib        0x00007fff78efb1ac basename_r + 0
7  swift                    0x000000010c44db74 (anonymous namespace)::MemoryToRegisters::promoteSingleAllocation(swift::AllocStackInst*, llvm::DenseMap<llvm::DomTreeNodeBase<swift::SILBasicBlock>*, unsigned int, llvm::DenseMapInfo<llvm::DomTreeNodeBase<swift::SILBasicBlock>*>, llvm::detail::DenseMapPair<llvm::DomTreeNodeBase<swift::SILBasicBlock>*, unsigned int> >&) + 9844
8  swift                    0x000000010c44b393 (anonymous namespace)::SILMem2Reg::run() + 1011
9  swift                    0x000000010c336673 swift::SILPassManager::runPassOnFunction(unsigned int, swift::SILFunction*) + 1475
10 swift                    0x000000010c3375c3 swift::SILPassManager::runFunctionPasses(unsigned int, unsigned int) + 1315
11 swift                    0x000000010c338284 swift::SILPassManager::execute() + 660
12 swift                    0x000000010bb4e88b swift::SILPassManager::executePassPipelinePlan(swift::SILPassPipelinePlan const&) + 187
13 swift                    0x000000010c340085 swift::runSILOptimizationPasses(swift::SILModule&) + 117
14 swift                    0x000000010b9e9680 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 13520
15 swift                    0x000000010b9e5161 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 3297
16 swift                    0x000000010b9a111c main + 2300
17 libdyld.dylib            0x00007fff78e87015 start + 1
Stack dump:
0.  Program arguments: /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2018-06-03-a.xctoolchain/usr/bin/swift -frontend -c -primary-file test.swift -target x86_64-apple-darwin17.5.0 -enable-objc-interop -O -color-diagnostics -module-name test -o /var/folders/50/br4kxvjd0t551h0fmtrzkwdw0000gn/T/test-847ef0.o 
1.  While running pass #565 SILFunctionTransform "Mem2Reg" on SILFunction "@$S4testAAyyF".
 for 'test()' at test.swift:4:1
<unknown>:0: error: unable to execute command: Abort trap: 6
<unknown>:0: error: compile command failed due to signal 6 (use -v to see invocation)

@effects applies LLVM-level annotations to the function, and makes it undefined behavior if your function doesn't follow the effects you declare. It's not documented because it's a low-level annotation and not intended for public consumption. (It really ought to be underscored.)

2 Likes

Please forgive my ignorance and curiosity, but how come eg this

@effects(readnone)
func f<T>(_ a: T) -> T where T: FixedWidthInteger { return a }

doesn't follow the effects I've declared, while afaict this does:

@effects(readnone)
func f(_ a: Int) -> Int { return a }

?

The effects I've declared is:

readnone

function has no side effects and no dependencies on the state of the program. It always returns an identical result given identical inputs. Calls to readnone functions can be eliminated, reordered, and folded arbitrarily.

readnone means that a function does not read or write memory at all. Unspecialized generic functions pass their arguments through memory so can never be readnone. readnone, and the other LLVM-level effects, is too low-level of a concept to be generally useful for Swift code.

Yes, my interest was sparked by this post from another thread:

And looking at nextUp's implementation as well as eg that of the new hashing, I sometimes need to write code at that level, so I thought it would be good to know more about @effects as part of my ongoing quest to get better at writing performance critical code (both in general and in Swift).

Does this mean that a generic function can never be marked @effects(readnone), or that it works only together with some other attribute(s) like @_specialize or @_transparent?

@effects in its current form isn't really designed for use outside of the standard library. I wouldn't worry about it. readnone enables optimizations that depend on being able to call a function fewer times than the source code formally specifies, such as hoisting a call out of a loop or removing a call whose result is not used, and these transformations can be very powerful for high-level abstractions, but in performance-oriented code they aren't anything you couldn't do yourself. If you have a for loop, you can move the expensive operations out of it manually, for instance.

Specialization doesn't guarantee that an argument isn't passed in memory either, so it isn't safe to use readnone at all.

Parden my lack of understanding, but how does this work with any function that takes arguments? Do you mean that anything passed by reference cannot be read and so only objects passed by value are readable and therefore usable in an @effects(readnone) function?

I assume this would also mean that if a struct were passed to a readnone function and you tried to read a class type parameter from the struct that the read would fail and you would have unpredictable results?

1 Like

readnone works at LLVM's level of abstraction. If you write a C or Swift function that takes machine-register-sized arguments and returns, like int add(int x, int y), and compiled that to LLVM, then the arguments x and y would be modeled as "registers" and not "memory". It's a bit harder to describe any behavior beyond that at the level of abstraction of Swift or C, since in the general case there's no guarantee whether an argument will be "in memory" or "in register" at the LLVM level.

3 Likes

So given how low-level and hard to reason this attribute is, and its limited use outside of the stdlib. Is it reasonable to rename this with a underscore and maybe disallow its use outside of the standard lib much the same way Builtin is?

From the examples I've seen posted above the potential for incorrect and dangerous behavior is too great for this kind of thing to look like a regular attribute that should be consumable to the general public.

1 Like

Yes, the attribute ought to have been underscored. I'm pretty sure that was an oversight on our part.

1 Like

I'm guessing that, rather unfortunately, this would require a SE-proposal at this point?