Validating ABI consistency between runtime C++ and standard library Swift code

I just tracked down a bug due to C++ code in the Swift runtime code trying to interface with standard library code written in Swift, but getting the ABI slightly wrong and leading to some nasty hard-to-reproduce heisenbugs. While I've narrowed down the bug in this specific case, it seems like this is a potentially continuing source of maintenance bugs, especially as we try to bring up the Swift calling convention in the coming year. I'm wondering if there's anything we could do to help validate that C++ and Swift code in the runtime is correctly interfacing. We could at least check that we're passing the right number of arguments by comparing the LLVM IR of the runtime and stdlib, perhaps, though this would have to be a fuzzy type-level match since Clang and Swift aren't normally going to agree on the exact pointer types of arguments.

-Joe

Can you directly pass arguments to the function crossing the ABI boundary, and can you directly access them on the other side? If so, what you can do is simply synthesize arguments that cross the full range of the bit-representation of the argument types, and validate that you see the same range on the other side.

I don't know anything about what this interface looks like so I don't have more specific ideas, but if you can give me a little more context I've worked on this in the past...

Technically you can't necessarily compare the LLVM IR, since there are ways to represent the same ABI call with different #s of IR arguments in corner cases.

- Daniel

···

On Oct 11, 2016, at 8:10 PM, Joe Groff via swift-dev <swift-dev@swift.org> wrote:

I just tracked down a bug due to C++ code in the Swift runtime code trying to interface with standard library code written in Swift, but getting the ABI slightly wrong and leading to some nasty hard-to-reproduce heisenbugs. While I've narrowed down the bug in this specific case, it seems like this is a potentially continuing source of maintenance bugs, especially as we try to bring up the Swift calling convention in the coming year. I'm wondering if there's anything we could do to help validate that C++ and Swift code in the runtime is correctly interfacing. We could at least check that we're passing the right number of arguments by comparing the LLVM IR of the runtime and stdlib, perhaps, though this would have to be a fuzzy type-level match since Clang and Swift aren't normally going to agree on the exact pointer types of arguments.

-Joe
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

One potential approach: make a def file with the names and (somehow abstracted) expected prototypes of Swift functions defined by the standard library but used by the runtime. In the runtime, use that to autogenerate a header. In IRGen, add a +Asserts check before finalizing the module that, if there are any functions with those names, they do match the abstract prototypes. Hope that all such interactions use only portable IRGen call-lowering patterns.

John.

···

On Oct 11, 2016, at 8:10 PM, Joe Groff via swift-dev <swift-dev@swift.org> wrote:
I just tracked down a bug due to C++ code in the Swift runtime code trying to interface with standard library code written in Swift, but getting the ABI slightly wrong and leading to some nasty hard-to-reproduce heisenbugs. While I've narrowed down the bug in this specific case, it seems like this is a potentially continuing source of maintenance bugs, especially as we try to bring up the Swift calling convention in the coming year. I'm wondering if there's anything we could do to help validate that C++ and Swift code in the runtime is correctly interfacing. We could at least check that we're passing the right number of arguments by comparing the LLVM IR of the runtime and stdlib, perhaps, though this would have to be a fuzzy type-level match since Clang and Swift aren't normally going to agree on the exact pointer types of arguments.

I don't know if this helps, but I think in the long run we'd like to
code more of the runtime directly in Swift, for optimization purposes if
nothing else. It won't make the problem go away completely, of course.

···

on Tue Oct 11 2016, Joe Groff <swift-dev-AT-swift.org> wrote:

I just tracked down a bug due to C++ code in the Swift runtime code
trying to interface with standard library code written in Swift, but
getting the ABI slightly wrong and leading to some nasty
hard-to-reproduce heisenbugs. While I've narrowed down the bug in this
specific case, it seems like this is a potentially continuing source
of maintenance bugs, especially as we try to bring up the Swift
calling convention in the coming year. I'm wondering if there's
anything we could do to help validate that C++ and Swift code in the
runtime is correctly interfacing. We could at least check that we're
passing the right number of arguments by comparing the LLVM IR of the
runtime and stdlib, perhaps, though this would have to be a fuzzy
type-level match since Clang and Swift aren't normally going to agree
on the exact pointer types of arguments.

--
-Dave

getting the ABI slightly wrong

Does this call for something like a validation pass? It would seem
appropriate to apply it to SIL and not LLVM IR since it's inter-language
ABI specific to Swift frontend.

···

On Tue, Oct 11, 2016 at 8:17 PM, Daniel Dunbar via swift-dev < swift-dev@swift.org> wrote:

Can you directly pass arguments to the function crossing the ABI boundary,
and can you directly access them on the other side? If so, what you can do
is simply synthesize arguments that cross the full range of the
bit-representation of the argument types, and validate that you see the
same range on the other side.

I don't know anything about what this interface looks like so I don't have
more specific ideas, but if you can give me a little more context I've
worked on this in the past...

Technically you can't necessarily compare the LLVM IR, since there are
ways to represent the same ABI call with different #s of IR arguments in
corner cases.

- Daniel

> On Oct 11, 2016, at 8:10 PM, Joe Groff via swift-dev < > swift-dev@swift.org> wrote:
>
> I just tracked down a bug due to C++ code in the Swift runtime code
trying to interface with standard library code written in Swift, but
getting the ABI slightly wrong and leading to some nasty hard-to-reproduce
heisenbugs. While I've narrowed down the bug in this specific case, it
seems like this is a potentially continuing source of maintenance bugs,
especially as we try to bring up the Swift calling convention in the coming
year. I'm wondering if there's anything we could do to help validate that
C++ and Swift code in the runtime is correctly interfacing. We could at
least check that we're passing the right number of arguments by comparing
the LLVM IR of the runtime and stdlib, perhaps, though this would have to
be a fuzzy type-level match since Clang and Swift aren't normally going to
agree on the exact pointer types of arguments.
>
> -Joe
> _______________________________________________
> swift-dev mailing list
> swift-dev@swift.org
> https://lists.swift.org/mailman/listinfo/swift-dev

_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Or perhaps have a swiftc mode that generates a C prototype for every function marked with @_silgen_name, and use that to build an autogenerated C header. (I.e., skip the def file and use the swift file as the canonical description.)

···

On Oct 12, 2016, at 11:11 AM, John McCall via swift-dev <swift-dev@swift.org> wrote:

On Oct 11, 2016, at 8:10 PM, Joe Groff via swift-dev <swift-dev@swift.org> wrote:
I just tracked down a bug due to C++ code in the Swift runtime code trying to interface with standard library code written in Swift, but getting the ABI slightly wrong and leading to some nasty hard-to-reproduce heisenbugs. While I've narrowed down the bug in this specific case, it seems like this is a potentially continuing source of maintenance bugs, especially as we try to bring up the Swift calling convention in the coming year. I'm wondering if there's anything we could do to help validate that C++ and Swift code in the runtime is correctly interfacing. We could at least check that we're passing the right number of arguments by comparing the LLVM IR of the runtime and stdlib, perhaps, though this would have to be a fuzzy type-level match since Clang and Swift aren't normally going to agree on the exact pointer types of arguments.

One potential approach: make a def file with the names and (somehow abstracted) expected prototypes of Swift functions defined by the standard library but used by the runtime. In the runtime, use that to autogenerate a header. In IRGen, add a +Asserts check before finalizing the module that, if there are any functions with those names, they do match the abstract prototypes. Hope that all such interactions use only portable IRGen call-lowering patterns.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

That's nice in theory, but it creates a build dependency between the C++ and Swift sources wherever we want to do this.

John.

···

On Oct 12, 2016, at 3:46 PM, Greg Parker <gparker@apple.com> wrote:

On Oct 12, 2016, at 11:11 AM, John McCall via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

On Oct 11, 2016, at 8:10 PM, Joe Groff via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:
I just tracked down a bug due to C++ code in the Swift runtime code trying to interface with standard library code written in Swift, but getting the ABI slightly wrong and leading to some nasty hard-to-reproduce heisenbugs. While I've narrowed down the bug in this specific case, it seems like this is a potentially continuing source of maintenance bugs, especially as we try to bring up the Swift calling convention in the coming year. I'm wondering if there's anything we could do to help validate that C++ and Swift code in the runtime is correctly interfacing. We could at least check that we're passing the right number of arguments by comparing the LLVM IR of the runtime and stdlib, perhaps, though this would have to be a fuzzy type-level match since Clang and Swift aren't normally going to agree on the exact pointer types of arguments.

One potential approach: make a def file with the names and (somehow abstracted) expected prototypes of Swift functions defined by the standard library but used by the runtime. In the runtime, use that to autogenerate a header. In IRGen, add a +Asserts check before finalizing the module that, if there are any functions with those names, they do match the abstract prototypes. Hope that all such interactions use only portable IRGen call-lowering patterns.

Or perhaps have a swiftc mode that generates a C prototype for every function marked with @_silgen_name, and use that to build an autogenerated C header. (I.e., skip the def file and use the swift file as the canonical description.)

I just tracked down a bug due to C++ code in the Swift runtime code trying to interface with standard library code written in Swift, but getting the ABI slightly wrong and leading to some nasty hard-to-reproduce heisenbugs. While I've narrowed down the bug in this specific case, it seems like this is a potentially continuing source of maintenance bugs, especially as we try to bring up the Swift calling convention in the coming year. I'm wondering if there's anything we could do to help validate that C++ and Swift code in the runtime is correctly interfacing. We could at least check that we're passing the right number of arguments by comparing the LLVM IR of the runtime and stdlib, perhaps, though this would have to be a fuzzy type-level match since Clang and Swift aren't normally going to agree on the exact pointer types of arguments.

One potential approach: make a def file with the names and (somehow abstracted) expected prototypes of Swift functions defined by the standard library but used by the runtime. In the runtime, use that to autogenerate a header. In IRGen, add a +Asserts check before finalizing the module that, if there are any functions with those names, they do match the abstract prototypes. Hope that all such interactions use only portable IRGen call-lowering patterns.

Or perhaps have a swiftc mode that generates a C prototype for every function marked with @_silgen_name, and use that to build an autogenerated C header. (I.e., skip the def file and use the swift file as the canonical description.)

That's nice in theory, but it creates a build dependency between the C++ and Swift sources wherever we want to do this.

I have not thought about the build dependency issue, but just off the top of my head, using it for building seems unnecessary. That is we could instead of using the autogenerated C header file for compilation, just generate those files for testing purposes. Then we could still use the .def file normally, but could at least have a bot that checks that things are correct.

I am not sure if there is a clang mode for diffing/comparing declarations like these though.

Michael

···

On Oct 12, 2016, at 4:36 PM, John McCall via swift-dev <swift-dev@swift.org> wrote:

On Oct 12, 2016, at 3:46 PM, Greg Parker <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Oct 12, 2016, at 11:11 AM, John McCall via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

On Oct 11, 2016, at 8:10 PM, Joe Groff via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

John.
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

I just tracked down a bug due to C++ code in the Swift runtime code trying to interface with standard library code written in Swift, but getting the ABI slightly wrong and leading to some nasty hard-to-reproduce heisenbugs. While I've narrowed down the bug in this specific case, it seems like this is a potentially continuing source of maintenance bugs, especially as we try to bring up the Swift calling convention in the coming year. I'm wondering if there's anything we could do to help validate that C++ and Swift code in the runtime is correctly interfacing. We could at least check that we're passing the right number of arguments by comparing the LLVM IR of the runtime and stdlib, perhaps, though this would have to be a fuzzy type-level match since Clang and Swift aren't normally going to agree on the exact pointer types of arguments.

One potential approach: make a def file with the names and (somehow abstracted) expected prototypes of Swift functions defined by the standard library but used by the runtime. In the runtime, use that to autogenerate a header. In IRGen, add a +Asserts check before finalizing the module that, if there are any functions with those names, they do match the abstract prototypes. Hope that all such interactions use only portable IRGen call-lowering patterns.

Or perhaps have a swiftc mode that generates a C prototype for every function marked with @_silgen_name, and use that to build an autogenerated C header. (I.e., skip the def file and use the swift file as the canonical description.)

That's nice in theory, but it creates a build dependency between the C++ and Swift sources wherever we want to do this.

I have not thought about the build dependency issue, but just off the top of my head, using it for building seems unnecessary. That is we could instead of using the autogenerated C header file for compilation, just generate those files for testing purposes. Then we could still use the .def file normally, but could at least have a bot that checks that things are correct.

I am not sure if there is a clang mode for diffing/comparing declarations like these though.

If we have to have a .def file, just consuming it for assertions in IRGen seems like the way to go.

John.

···

On Oct 13, 2016, at 9:30 AM, Michael Gottesman <mgottesman@apple.com> wrote:

On Oct 12, 2016, at 4:36 PM, John McCall via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

On Oct 12, 2016, at 3:46 PM, Greg Parker <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Oct 12, 2016, at 11:11 AM, John McCall via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

On Oct 11, 2016, at 8:10 PM, Joe Groff via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

Michael

John.
_______________________________________________
swift-dev mailing list
swift-dev@swift.org <mailto:swift-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-dev