-
Notifications
You must be signed in to change notification settings - Fork 913
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
compiler: implement deferred calls to sync/atomic.* #2821
Conversation
@aykevl I added tests for each individual call to the sync/atomic.* package. Would you be willing to review this? I think it is pretty straightforward, as it is leveraging the existing code for rewriting atomics (no changes there). The build failures seem completely unrelated, maybe due to go 1.18, or some CI configuration changes to windows. |
I think windows is failing due to an upstream scoop issue: |
I think #2853 will resolve build issues 🤞 |
@skabbes please rebase against |
@deadprogram all green! |
The rewrite pass for atomics only rewrote "regular" function calls to sync/atomic.*, but was missed when generating `defer` handlers.
FYI - as deadprogram has requested of me on other PRs - I can clean up / squash / whatever the commits once we have completed a code review. |
While this probably works fine, I think there is a much more elegant way: by defining the functions (like Here is how to do that. First you can detect the special sync/atomic functions here: diff --git a/compiler/compiler.go b/compiler/compiler.go
index dd7a8ea3..6981fc06 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -773,6 +773,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
// Create the function definition.
b := newBuilder(c, irbuilder, member)
if member.Blocks == nil {
+ if member.Pkg.Pkg.Path() == "sync/atomic" && token.IsExported(member.Name()) {
+ b.implementAtomicOp()
+ }
continue // external function
}
b.createFunction() And then you can fill them with atomic instructions here: func (b *builder) implementAtomicOp() {
entry := b.ctx.AddBasicBlock(b.llvmFn, "entry")
b.Builder.SetInsertPointAtEnd(entry)
switch b.fn.Name() {
case "AddInt32":
ptr := b.llvmFn.Param(0)
val := b.llvmFn.Param(1)
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
// Return the new value, not the original value returned by atomicrmw.
newVal := b.CreateAdd(oldVal, val, "")
b.CreateRet(newVal)
default:
b.addError(b.fn.Pos(), "unknown atomic operation: "+b.fn.Name())
b.CreateUnreachable()
}
} (This is not complete of course, it only implements |
OK, thanks - I don't think I'll be able to get around to the changes. But maybe someone else can pick up the slack. |
Closing as this was implemented in #2920 |
Fixes: #2652 , #2805
And is part of a larger effort to get Protobufs working with tinygo. #2667
Previously discussed here, and here.
Problem
Deferrred calls to the sync/atomic package would fail with a linker error, since those functions don't really exist in the LLVM representation.
Solution
After some discussion in the referenced issues, the solution I pursued is to share the previous rewrite code with
createRunDefers
. (In effect the deferred calls to atomic.* are inlined in exactly the same way).Next Steps
The amount of code changed is small, and easy to understand, and given this has never worked before - I am fairly confident it won't break anything. The tests 'prove' it works, but given the "run defers" code seems particularly hairy (I would love a second set of eyes on it).
@aykevl this is a slight modification of your original idea here, do you mind taking a look?
Please feel free to hack away at my code if you'd find that faster than coordinating the work with me over comments.