What Is '@_Silgen_Name' in Swift Language

What is '@_silgen_name' in Swift language?

@_silgen_name was just recently renamed from @asmname (see the following commit) with the following commit message:

This reflects the fact that the attribute's only for
compiler-internal use
, and isn't really equivalent to C's asm
attribute, since it doesn't change the calling convention to be
C-compatible.

So as a general Swift developer, one wouldn't come across this attribute, unless working with, say, porting Swift to some other platform.

Now, @_silgen_name is an attribute (macro) for the class SILGenNameAttr with certain options, where the latter is part of Swifts abstract syntax tree (AST). From the swift/AST/Attr.def source code (see also swift/lib/AST/Attr.cpp)

// Schema for DECL_ATTR:
//
// - Attribute name.
// - Class name without the 'Attr' suffix (ignored for
// - Options for the attribute, including:
// * the declarations the attribute can appear on
// * whether duplicates are allowed
// * whether the attribute is considered a decl modifier or not (no '@')
// - Unique attribute identifier used for serialization. This
// can never be changed.
//
// SIMPLE_DECL_ATTR is the same, but the class becomes
// SimpleDeclAttr<DAK_##NAME>.
//

DECL_ATTR(_silgen_name, SILGenName,
OnFunc | OnConstructor | OnDestructor | LongAttribute |
UserInaccessible, 0)

We find the declaration of SILGeneNameAttr in swift/AST/Attr.h:

/// Defines the @_silgen_name attribute.
class SILGenNameAttr : public DeclAttribute {
public:
SILGenNameAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
: DeclAttribute(DAK_SILGenName, AtLoc, Range, Implicit),
Name(Name) {}

SILGenNameAttr(StringRef Name, bool Implicit)
: SILGenNameAttr(Name, SourceLoc(), SourceRange(), /*Implicit=*/true) {}

/// The symbol name.
const StringRef Name;

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_SILGenName;
}
};

To sum up; it's related to provide a Swift interface for C functions. You will most likely have a hard time finding any details regarding SILGenNameAttr in the developer library, and can consider it an undocumented feature.


Finally, the following talk with Russ Bishop might be of interest to you:

  • Unsafe Swift: For Fun & Profit.

The Toolbox: Here there be dragons (34:20)

I would not ship this in a production application under any
circumstances. But if you’re feeling adventurous, here they are.

@asmname is an attribute to decorate a function. You definitely need
to understand the ABI to use this one. Swift will not help you
marshall the parameters very much. The compiler is not going to be
very forgiving, so you have to make sure that you’ve manipulated your
parameters into a format it’s compatible with. The following code
shows how to declare it. Give the function attribute, @asmname, and
the string symbol; then function its arguments in return type. The
compiler will not complain if you get the arguments wrong or return
type incorrectly. It only expects that that symbol exists, and when it
jumps to it, it better take those arguments and have that return type.

@asmname("dispatch_get_current_queue") func _get_current_queue() -> dispatch_queue_t

Q&A (37:08)

Q: Obviously not all of these are documented by Apple, so what’s your process of discovering all these behaviors?

Russ: I’ll look at the documentation first, but you’re right that there is a lot of stuff that isn’t in there. If you go to the Swift
REPL and use the flag — I think it’s something like
-deprecated-integrated-repl — you can ask it to print the Swift module and all the bits that Xcode doesn’t show you. If you dig into the
toolchain directory Xcode, you can also find stuff in libswiftCore and
libswiftRuntime.

JP: If you’re interested in doing some more unsafe things with Swift, you can do a code search in GitHub for @asmname and language
“Swift”, and you’ll see a bunch of really bad but interesting stuff.

A slightly older post from Bishops' blog:

  • Swift - Don't do this.

...

First off notice the @asmname attribute. This is the equivalent of
DllImport or extern. It tells Swift that we are going to link in
some library that defines a function with the given name and matching
the given arguments. "Just trust me Swift, I know what I'm doing".
Hint: You had better know what you're doing.

Can I use @_silgen_name to expose an [Objective-]C or C++ symbol that is not a function to Swift?

As of writing (August 8th, 2017), this currently is not possible with any version of Swift (3 or 4).

Without a header declaration, clang isn't sure how to map something in Swift back to C. @_silgen_name and @_cdecl work on functions, but not for variables.

This was confirmed by Jordan Rose, a Swift compiler engineer at Apple on Twitter.

tl;dr:

anything but func is hard.

How are Int and String accepted as AnyHashable?

Consider that Optional is an enum, which is also a value type – and yet you're freely able to convert a String to an Optional<String>. The answer is simply that the compiler implicitly performs these conversions for you.

If we look at the SIL emitted for the following code:

let i: AnyHashable = 5

We can see that the compiler inserts a call to _swift_convertToAnyHashable:

  // allocate memory to store i, and get the address.
alloc_global @main.i : Swift.AnyHashable, loc "main.swift":9:5, scope 1 // id: %2
%3 = global_addr @main.i : Swift.AnyHashable : $*AnyHashable, loc "main.swift":9:5, scope 1 // user: %9

// allocate temporary storage for the Int, and intialise it to 5.
%4 = alloc_stack $Int, loc "main.swift":9:22, scope 1 // users: %7, %10, %9
%5 = integer_literal $Builtin.Int64, 5, loc "main.swift":9:22, scope 1 // user: %6
%6 = struct $Int (%5 : $Builtin.Int64), loc "main.swift":9:22, scope 1 // user: %7
store %6 to %4 : $*Int, loc "main.swift":9:22, scope 1 // id: %7

// call _swift_convertToAnyHashable, passing in the address of i to store the result, and the address of the temporary storage for the Int.
// function_ref _swift_convertToAnyHashable
%8 = function_ref @_swift_convertToAnyHashable : $@convention(thin) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0) -> @out AnyHashable, loc "main.swift":9:22, scope 1 // user: %9
%9 = apply %8<Int>(%3, %4) : $@convention(thin) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0) -> @out AnyHashable, loc "main.swift":9:22, scope 1

// deallocate temporary storage.
dealloc_stack %4 : $*Int, loc "main.swift":9:22, scope 1 // id: %10

Looking in AnyHashable.swift, we can see the function with the silgen name of _swift_convertToAnyHashable, which simply invokes AnyHashable's initialiser.

@_silgen_name("_swift_convertToAnyHashable")
public // COMPILER_INTRINSIC
func _convertToAnyHashable<H : Hashable>(_ value: H) -> AnyHashable {
return AnyHashable(value)
}

Therefore the above code is just equivalent to:

let i = AnyHashable(5)

Although it's curious that the standard library also implements an extension for Dictionary (which @OOPer shows), allowing for a dictionary with a Key of type AnyHashable to be subscripted by any _Hashable conforming type (I don't believe there are any types that conform to _Hashable, but not Hashable).

The subscript itself should work fine without a special overload for _Hashable keys. Instead the default subscript (which would take an AnyHashable key) could just be used with the above implicit conversion, as the following example shows:

struct Foo {
subscript(hashable: AnyHashable) -> Any {
return hashable.base
}
}

let f = Foo()
print(f["yo"]) // yo

Edit: In Swift 4, both the aforementioned subscript overload and _Hashable have been removed from the stdlib by this commit with the description:

We have an implicit conversion to AnyHashable, so there's no
need to have the special subscript on Dictionary at all.

Which confirms my suspicion.

Weird value of #function literal in Swift 3.1

Swift 5 update

In Swift 5 (not officially released yet, but you can pick up a master snapshot), this inconsistency is fixed thanks to #19062. Your code now outputs the following:

functionLiteralTest.weirdo()             // returns "weirdo()"
functionLiteralTest.weirdo(parameter: 1) // returns "weirdo(parameter:)"
functionLiteralTest.weirdo(1) // returns "weirdo(_:)"
functionLiteralTest.weirdo(1, 2) // returns "weirdo(_:_:)"

Pre Swift 5

I agree this is completely baffling behaviour, but it does appear to be intentional.

Function literals get "filled in" in the process of SILGen; and this is done through the SILGenFunction::emitLiteral function in SILGenApply.cpp.

This then calls onto getMagicFunctionString for a function literal:

static StringRef
getMagicFunctionString(SILGenFunction &SGF) {
assert(SGF.MagicFunctionName
&& "asking for #function but we don't have a function name?!");
if (SGF.MagicFunctionString.empty()) {
llvm::raw_string_ostream os(SGF.MagicFunctionString);
SGF.MagicFunctionName.printPretty(os);
}
return SGF.MagicFunctionString;
}

Which, if not already generated, creates a new stream to output to MagicFunctionString, and calls DeclName::printPretty on MagicFunctionName with this stream:

llvm::raw_ostream &DeclName::printPretty(llvm::raw_ostream &os) const {
return print(os, /*skipEmptyArgumentNames=*/true);
}

(MagicFunctionName is assigned when the function is emitted; and is given the value of getFullName(), which is just the Name of the declaration)

This then calls onto DeclName::print, which as its second parameter takes a boolean argument to determine whether to skip listing argument names if they're all empty:

llvm::raw_ostream &DeclName::print(llvm::raw_ostream &os,
bool skipEmptyArgumentNames) const {
// Print the base name.
os << getBaseName();

// If this is a simple name, we're done.
if (isSimpleName())
return os;

if (skipEmptyArgumentNames) {
// If there is more than one argument yet none of them have names,
// we're done.
if (getArgumentNames().size() > 0) {
bool anyNonEmptyNames = false;
for (auto c : getArgumentNames()) {
if (!c.empty()) {
anyNonEmptyNames = true;
break;
}
}

if (!anyNonEmptyNames)
return os;
}
}

// Print the argument names.
os << "(";
for (auto c : getArgumentNames()) {
os << c << ':';
}
os << ")";
return os;

}

And you can see that because of the if condition if (getArgumentNames().size() > 0), functions with no parameters will skip the check for all empty argument names, leading them to being emitted with parentheses, e.g weirdo(). But functions with one or more parameters, all with empty argument names, get emitted without parenthesis, e.g weirdo.

So, given DeclName::printPretty specifically passes true for the skipEmptyArgumentNames argument, it seems that the Swift team do specifically want this behaviour for #function.

Furthermore, if we look at the blame, we can see that DeclName::printPretty was added in this commit, with the commit message:

Pretty-print DeclNames with no keyword arguments by dropping the
parenthsized bit.

Instead of printing "f(_:_:)", just print "f".

That being said, I would still file a bug report over it, as it doesn't seem that intuitive for function literals.

What is #tfop in Swift Tensorflow and where is it defined?

per Chris Lattner:

#tfop is a “well known” representation used for tensor operations.
It is an internal implementation detail of our stack that isn’t meant
to be user visible, and is likely to change over time.

In Swift, "#foo(bar: 42)” is the general syntax used for “macro like”
and “compiler magic” operations. For example C things like FILE
are spelled as #file in swift:
https://github.com/apple/swift-evolution/blob/master/proposals/0034-disambiguating-line.md

And the “#line 42” syntax used by the C preprocesser is represented
with arguments like this: #sourceLocation(file: "foo", line: 42)

In the case of #tfop specifically, this is represented in the Swift
AST as an ObjectLiteralExpr, which is the normal AST node for this
sort of thing:
https://github.com/google/swift/blob/tensorflow/include/swift/AST/Expr.h#L1097

We use special lowering magic to turn it into a SIL builtin
instruction in SILGen, which are prefixed with "__tfop_"
https://github.com/google/swift/blob/tensorflow/lib/SILGen/SILGenExpr.cpp#L3009

I’d like to move away from using builtin instructions for this, and
introduce a first-class sil instruction instead, that’s tracked by:
https://github.com/google/swift/issues/16

These instructions are specially recognized by the partitioning pass
of GPE:
https://github.com/google/swift/blob/tensorflow/lib/SILOptimizer/Mandatory/TFUtilities.cpp#L715

source here

Why Choose Struct Over Class?

According to the very popular WWDC 2015 talk Protocol Oriented Programming in Swift (video, transcript), Swift provides a number of features that make structs better than classes in many circumstances.

Structs are preferable if they are relatively small and copiable because copying is way safer than having multiple references to the same instance as happens with classes. This is especially important when passing around a variable to many classes and/or in a multithreaded environment. If you can always send a copy of your variable to other places, you never have to worry about that other place changing the value of your variable underneath you.

With Structs, there is much less need to worry about memory leaks or multiple threads racing to access/modify a single instance of a variable. (For the more technically minded, the exception to that is when capturing a struct inside a closure because then it is actually capturing a reference to the instance unless you explicitly mark it to be copied).

Classes can also become bloated because a class can only inherit from a single superclass. That encourages us to create huge superclasses that encompass many different abilities that are only loosely related. Using protocols, especially with protocol extensions where you can provide implementations to protocols, allows you to eliminate the need for classes to achieve this sort of behavior.

The talk lays out these scenarios where classes are preferred:

  • Copying or comparing instances doesn't make sense (e.g., Window)
  • Instance lifetime is tied to external effects (e.g., TemporaryFile)
  • Instances are just "sinks"--write-only conduits to external state (e.g.CGContext)

It implies that structs should be the default and classes should be a fallback.

On the other hand, The Swift Programming Language documentation is somewhat contradictory:

Structure instances are always passed by value, and class
instances are always passed by reference. This means that they are
suited to different kinds of tasks. As you consider the data
constructs and functionality that you need for a project, decide
whether each data construct should be defined as a class or as a
structure.

As a general guideline, consider creating a structure when one or more
of these conditions apply:

  • The structure’s primary purpose is to encapsulate a few relatively simple data values.
  • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an
    instance of that structure.
  • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
  • The structure does not need to inherit properties or behavior from another existing type.

Examples of good candidates for structures include:

  • The size of a geometric shape, perhaps encapsulating a width property and a height property, both of type Double.
  • A way to refer to ranges within a series, perhaps encapsulating a start property and a length property, both of type Int.
  • A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double.

In all other cases, define a class, and create instances of that class
to be managed and passed by reference. In practice, this means that
most custom data constructs should be classes, not structures.

Here it is claiming that we should default to using classes and use structures only in specific circumstances. Ultimately, you need to understand the real world implication of value types vs. reference types and then you can make an informed decision about when to use structs or classes. Also, keep in mind that these concepts are always evolving and The Swift Programming Language documentation was written before the Protocol Oriented Programming talk was given.



Related Topics



Leave a reply



Submit