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

cabi: add Canonical ABI realloc (cabi_realloc) #3

Merged
merged 20 commits into from
Oct 23, 2023
Merged

cabi: add Canonical ABI realloc (cabi_realloc) #3

merged 20 commits into from
Oct 23, 2023

Conversation

ydnar
Copy link
Collaborator

@ydnar ydnar commented Oct 17, 2023

This PR adds a function exported from the Wasm module named cabi_realloc used by the Component Model to allocate memory in the guest for host → guest function calls.

Fixes #2.

@ydnar
Copy link
Collaborator Author

ydnar commented Oct 17, 2023

This is WIP, but probably safe to merge

@ydnar ydnar marked this pull request as ready for review October 17, 2023 23:13
@ydnar ydnar self-assigned this Oct 17, 2023
Copy link
Member

@Mossaka Mossaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Asked a few questions for myself to understand the implementation.

abi/realloc.go Outdated
//go:wasmexport cabi_realloc
func realloc(ptr unsafe.Pointer, size, align, newsize uintptr) unsafe.Pointer {
if newsize <= size {
return unsafe.Add(ptr, offset(uintptr(ptr), align))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that when newsize <= size, it returns the original pointer with additional offset. But is this really working without reallocating and copying the old memory block to a new memory block?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s an interesting example. If you realloc with a different alignment than the original allocation, presumably you’re storing a different type, in which case the copy shouldn’t happen. What does the spec say here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a spec for this realloc above and beyond what the C specification has? (If so, should we link to it?).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md#canonical-abi

The (realloc ...) option specifies a core function that is validated to have the following core function type:

(func (param $originalPtr i32)
      (param $originalSize i32)
      (param $alignment i32)
      (param $newSize i32)
      (result i32))

The Canonical ABI will use realloc both to allocate (passing 0 for the first two parameters) and reallocate. If the Canonical ABI needs realloc, validation requires this option to be present (there is no default).

abi/realloc.go Outdated
}
newptr := alloc(newsize, align)
if size > 0 {
copy(unsafe.Slice((*byte)(newptr), newsize), unsafe.Slice((*byte)(ptr), size))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not deallocating the old memory block after copying?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How? :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While standard Go doesn't have a the equivalent of free(), since we're deferring to Go's runtime in both cases we should allow them to be garbage collected if possible. Unfortunately, there's no easy way to null out all references to the old pointer -- we just have to trust the client doesn't hold on to it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mossaka does the spec confirm that once the memory is allocated with realloc and the pointer is passed to a guest func, the memory is owned by the guest?

@dgryski will using unsafe.Pointer in go:wasmexport function signatures be sufficient to signal to the compiler that the value passed in is a pointer that can be GCed if not used?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For garbage collection, if there are no references to the pointer in the guest address space, then it will be collected. As with other uses of realloc, we'll need to be careful to keep track of the pointer in the guest if we don't want it accidentally reclaimed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i didn't find in the CM spec where it says the memory allocated with realloc will be owned by the guest...

abi/realloc.go Outdated
//
//go:export cabi_realloc
//go:wasmexport cabi_realloc
func realloc(ptr unsafe.Pointer, size, align, newsize uintptr) unsafe.Pointer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if allocation failed due to out of memory issue?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the runtime will panic? What do you think is best way to handle this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both Go and TinyGo panic with out of memory if they can't allocate a block.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a runtime panic acceptable to the Component Model if it can't allocate memory?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ydnar There is no way to capture that allocation failure and retry or notify the caller. It's an immediate, uncatchable, runtime panic(). We don't have a choice in this case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A runtime failure seems reasonable to me.

abi/realloc.go Outdated
s := make([]uint64, min(size/align, 1))
return unsafe.Pointer(unsafe.SliceData(s))
default:
s := make([][16]uint8, min(size/align, 1))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking this should be a struct or complex128.

abi/realloc.go Outdated
//go:export cabi_realloc
//go:wasmexport cabi_realloc
func realloc(ptr unsafe.Pointer, size, align, newsize uintptr) unsafe.Pointer {
if newsize <= size {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you want to check if ptr is not nil, or uintptr(ptr) != 0?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If newsize is 0, and ptr is 0, then it will always return 0. This is somewhat based on the Rust implementation.

@ydnar ydnar changed the title abi: add Realloc for Component Model ABI abi: add Canonical ABI realloc (cabi_realloc) Oct 19, 2023
Copy link
Member

@Mossaka Mossaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@ydnar ydnar changed the title abi: add Canonical ABI realloc (cabi_realloc) cabi: add Canonical ABI realloc (cabi_realloc) Oct 22, 2023
@ydnar ydnar merged commit cedd8b0 into main Oct 23, 2023
1 check passed
@ydnar ydnar deleted the ydnar/realloc branch October 23, 2023 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

proposal: implement Canonical ABI realloc (cabi_realloc)
3 participants